Jeans & Development / Nucleus https://www.rad51.net/jeans/ コンピューターのことなどを綴ったメモ (旧:目から鱗 w/SQLite) / ブログツール Nucleus について ja Jeans CMS © Weblog http://backend.userland.com/rss https://www.rad51.net/jeans/skins/jeans/images/jeans2.gif Jeans & Development https://www.rad51.net/jeans/ Nucleus CMS ver 3.62に、XSS脆弱性 https://www.rad51.net/jeans/?itemid=815
japan.nucleuscms.org: Nucleus CMS ver 3.62 の createaccount.phpにXSS脆弱性
nucleuscms.org: Nucleus CMS 3.63 Released

それぞれ、次のようなケースで攻撃を受ける可能性があります。

英語版 3.62:
・"Configuration"の"Member Settings"で、"Allow Visitors to Create a Member Account"を"yes"にしている場合。

日本語版 3.62:
・「グローバル設定」の「メンバー設定」で、「ビジターによるメンバーアカウント作成を可能にする」を「はい」に設定していて、かつ、
・createaccount.phpのPHP文法上の誤りを修正して使っていて、かつ、
・PHPの設定で、registerglobals=ONとしている場合。

条件に当てはまる場合、英語版の場合は3.63以降にアップグレードしてください。日本語版の場合、パッチを当てるか、3.63以降にアップグレードしてください(2011年3月5日現在、3.63はまだリリースされていません)。]]>
Nucleus https://www.rad51.net/jeans/?itemid=815 Sat, 05 Mar 2011 13:27:59 PST ITEM815_20110305
Nucleus-SQLiteでのEUC-JPからUTF-8への文字コード変更 https://www.rad51.net/jeans/?itemid=759
まず、『nucleus/sqlite/.dbsqlite』ファイルのコピーを、バックアップとして取っておく。

SQLiteManagerを立ち上げる。データのExportページにて、

・Structure and Data
・Complete inserts
・send
・CRLF

を選択して、実行(Add "DROP"は選択しない)。

ダウンロードされたファイルをエディタで開き、UTF-8Nに変換した後『dbsqlite-no-drop-utf8.sql』のファイル名で保存。

以下のPHPスクリプトを保存したファイルと同じディレクトリに置き、実行する。

作成された『.dbsqlite』を、『nucleus/sqlite/』ディレクトリに移動する。

Nucleus管理画面グローバル設定にて、使用する言語を『japanese-utf8』に変更する(ただし、古いバージョンのNucleusの場合、UTF8バージョンのものに置き換える必要アリ)。

必要であれば、スキンのメタタグの言語指定を、EUC-JPからUTF-8に変更する。

PHPスクリプト:
<?php

$sqlfile=dirname(__FILE__).'/dbsqlite-no-drop-utf8.sql';
$dest=dirname(__FILE__).'/.dbsqlite';
if (file_exists($dest)) exit("File: '$dest' already exists!");

$textdata=file_get_contents($sqlfile);
if (strlen($textdata)==0) exit("Data not found: '$sqlfile'!");

if (!preg_match_all("/(?:[^';]+|'[^']*?')*;/",$textdata,$m, PREG_PATTERN_ORDER)) exit("Data not found");
$textdata=null;
$arraydata=$m[0];
$m=null;

$db=sqlite_open($dest);

sqlite_query($db,'BEGIN TRANSACTION');
foreach($arraydata as $query) {
    $query=trim($query);
    while(preg_match('/^(?:#[^\r\n]*[\r\n]+)/',$query,$m)) $query=substr($query,strlen($m[0]));
    sqlite_query($db,$query);
}
sqlite_query($db,'COMMIT');


P.S.
 ファイルの大きさの問題なのか、SQLiteManagerのSQLページでは、文字コードをUTF8に変換したsqlファイルを実行することが出来なかった。]]>
Nucleus https://www.rad51.net/jeans/?itemid=759 Sat, 24 Oct 2009 19:51:19 PDT ITEM759_20091024
NP_SkinExtensible https://www.rad51.net/jeans/?itemid=714
ソースコード及び使用方法は、Wikiを参照してください。注意点としては、NP_SkinVarManagerをインストールしておく必要があることです。

ここでは、どのようにすれば派生スキンが作成できるのか、一例を挙げておきます。

簡単な例として、デフォルトスキンのサイドバーを左に移したスキンを作成する場合を考えます。

1)まず、プラグインをインストールする。
 NP_SkinExtensible及びNP_SkinVarManagerをWikiのページから取ってきます。双方ともソースコードの形で公表してありますので、それぞれNP_SkinExtensible.php及びNP_SkinVarManager.phpという名のファイルにコピーペーストでコードを貼り付け、保存した後に、nucleus/plugins/ディレクトリにアップロードしてください。
 プラグインのインストールは、NP_SkinVarManager, NP_SkinExtensibleの順で行います。特に設定するべき事項はありません。

2)新しくスキンを作成。
 次に、派生スキンを新規スキンとして作成します。『スキン編集』ページに『新しいスキン』という項目があるので、ここで行います。例では、『名前』に『test』を指定して作成することにします。
 作成できたら、『test』の右の『編集』をクリックし、次のページで『Include モード』に『skindirを使う』を、『Include プリフィックス』に『test/』を指定し、『設定の変更』を押します。

3)派生元のスキンを指定。
 スキン『test』の『編集』画面に『スペシャルスキンパーツ』という項目があるので、ここに『extends』を入力し、『作成』を押します。次の画面では、『default』と入力し(このとき、左右に空白が入らないように注意)、『スキンの変更』を押します。これで、派生元のスキンとして、『default』が指定されました。
 ここまでくれば、新しい『test』スキンが有効になっています。index.php?skinid=testにアクセスすれば、『test』スキンが『default』スキンと同じように表示されるはずです。

4)派生スキンを編集。
 最後に、サイドバーを左に変更する操作です。この設定は、skins/default/head.incで行われていますので、まずこのファイルを、skins/test/head.incにコピーします。
 skins/test/head.incを編集します。19行目あたり、『<%skinfile(default_right.css)%>』を『<%skinfile(default_left.css)%>』変更してください。
 これで、すべて終了です。index.php?skinid=testにアクセスし、サイドバーが左に移ったデフォルトスキンが表示されることを確認してください。
]]>
Nucleus https://www.rad51.net/jeans/?itemid=714 Wed, 11 Mar 2009 15:02:39 PDT ITEM714_20090311
SQLクエリのキャッシュ:実験失敗 https://www.rad51.net/jeans/?itemid=700
Jeansのコードを書いているときに、Nucleusのコアのコードを眺めることがあるのだが、ふと思ったことは、同じSQLクエリーを繰り返し実行しているのではないかということ。これは正直、無駄である。なので、SQLクエリーをキャッシュして、再利用できないかを探ってみた。

Nucleusの場合、クエリーはglobalfunctions.phpにあるグローバル関数、sql_query()で行われている。次のようなコードだ。

function sql_query($query) {
    global $SQLCount;
    $SQLCount++;
    $res = mysql_query($query) or print("mySQL error with query $query: " . mysql_error() . '<p />');
    return $res;
}

これを、以下のように変更してみた。

function sql_query($query) {
    static $cache=array();
    if (isset($cache[$query])) {
        $res=$cache[$query];
        if (@mysql_data_seek($res,0)) return $res;
        else unset($cache[$query]);
    }
    global $SQLCount;
    $SQLCount++;
    $res = mysql_query($query) or print("mySQL error with query $query: " . mysql_error() . '<p />');
    if (mysql_num_rows($res)<5) $cache[$query]=$res;
    return $res;
}

リソースを、キャッシュとしてスタティック変数に残しておいて、同じクエリーが実行する場合は巻き戻して再利用しようというもの。もちろん、データベースに変更が加えられたときなどは同じクエリーでも結果が異なる。しかしながら、それについては後でコードを書き換えて対応することにして、とりあえず上記コードで実験してみた。

まず、Nucleusインストール直後のデフォルト状態で、クエリー実行回数が一回減ることを確認。気をよくして、このブログに試しに入れてみた結果……。クエリー実行回数は減らなかった。

Nucleusのクエリーの実行回数の多さは異常ともいえるほどなので、ゆくゆくは何か対処したい。Jeansでは、何らかのキャッシュ機構を是非導入したいと考えている。こういった仕組みについては、hsurさんのCACHEMANAGERがあるけれど、少し違った考え方で導入することを考えている。忘れないうちにアイデアだけここにメモしておくと、ようするにキャッシュ用のテーブルを一つ用意し、スキンパース前にそれらの内容を一気に取り込んでしまおうという物。テンポラリテーブルだとかの機能を利用することになりそう。fetchのさいにオブジェクトを利用しているJeansの方が、実装は容易かもしれない。]]>
Nucleus https://www.rad51.net/jeans/?itemid=700 Thu, 25 Dec 2008 16:09:00 PST ITEM700_20081225
NP_skinsetting https://www.rad51.net/jeans/?itemid=699 yama.kymsさんのNP_SkinEditを見ていて気がついたのは、ブログ情報表示のための<%blogsetting%>に相当する、スキン情報を表示するためのスキン変数がNucleusには無いこと。

もしあれば、何か便利かもしれない。今は思いつかないけれど。

なので、作ってみた。もしかしたら、似たプラグインがすでにあるかもしれない。

使い方:
<%skinsetting(id)%>
<%skinsetting(description)%>
<%skinsetting(contenttype)%>
<%skinsetting(includemode)%>
<%skinsetting(includeprefix)%>
<%skinsetting(name)%>
<%skinsetting(type)%>

高度な(?)使い方:
<%skinsetting(idfromname,[skin_name])%>
<%skinsetting(namefromid,[skin_id])%>
<%skinsetting(content,[skin_type])%>

ついでに、NP_otherskinsettingも作ってみた(NP_skinsettingが必要)。

NP_skinsetting.php
<?php 
class NP_skinsetting extends NucleusPlugin { 
    function getName() { return 'NP_skinsetting'; }
    function getMinNucleusVersion() { return 220; }
    function getAuthor()  { return 'Katsumi'; }
    function getVersion() { return '0.1'; }
    function getURL() {return 'http://japan.nucleuscms.org/wiki/plugins:authors:katsumi';}
    function getDescription() {
        return 'Usage:<br />
&lt;%skinsetting(id)%&gt;,
&lt;%skinsetting(description)%&gt;,
&lt;%skinsetting(contenttype)%&gt;,
&lt;%skinsetting(includemode)%&gt;,
&lt;%skinsetting(includeprefix)%&gt;,
&lt;%skinsetting(name)%&gt;, or
&lt;%skinsetting(type)%&gt;';
    } 
    function supportsFeature($what) { return ($what=='SqlTablePrefix')?1:0; }
    function getEventList() { return array('InitSkinParse'); }
    var $skinobj;
    function event_InitSkinParse(&$data) {
        $this->skinobj=&$data['skin'];
    }
    function doSkinVar($skintype,$type,$p1='') {
        if ($type=='type') $data=$skintype;
        elseif (method_exists($this->skinobj,"get$type")) {
            $data=call_user_func(array($this->skinobj,"get$type"),$p1);
        }
        echo htmlspecialchars($data,ENT_QUOTES);
    }
}

NP_ohterskinsetting.php
<?php 
class NP_otherskinsetting extends NucleusPlugin { 
    function getName() { return 'NP_otherskinsetting'; }
    function getMinNucleusVersion() { return 220; }
    function getAuthor()  { return 'Katsumi'; }
    function getVersion() { return '0.1'; }
    function getURL() {return 'http://japan.nucleuscms.org/wiki/plugins:authors:katsumi';}
    function getPluginDep() { return array('NP_skinsetting'); }
    function getDescription() {
        return str_replace('skinsetting(','otherskinsetting([skin_id/name],',
            $this->skinsetting->getDescription());
    }
    function supportsFeature($what) { return ($what=='SqlTablePrefix')?1:0; }
    var $skinsetting;
    function init(){
        global $manager;
        $this->skinsetting=$manager->getPlugin('NP_skinsetting');
    }
    function doSkinVar($skintype,$id,$type,$p1='') {
        static $objs=array();
        if (!is_numeric($id)) $id=SKIN::getIdFromName($id);
        $id=(int)$id;
        if (!isset($objs[$id])) {
            $objs[$id]=new NP_skinsetting;
            $obj=new SKIN($id);
            $data=array('type'=>$skintype,'skin'=>&$obj);
            $objs[$id]->event_InitSkinParse($data);
        }
        return $objs[$id]->doSkinVar($skintype,$type,$p1);
    }
}
]]>
Nucleus https://www.rad51.net/jeans/?itemid=699 Tue, 23 Dec 2008 15:59:27 PST ITEM699_20081223
Nucleusメディアマネージャにおける脆弱性 https://www.rad51.net/jeans/?itemid=697 Nucleusのメディアマネージャ(コアに付属のもの及び、プラグインによるもの)に、ディレクトリ・トラバーサル等の脆弱性が発見されています。

詳しくは、フォーラムの当該記事をご覧ください。主に、複数ユーザでNucleusを用いているときに、大きな問題が生じます。コアについてはすでにパッチ(SP3)が出ていますので、複数ユーザで使用の方は3.31 SP3にアップグレードしてください。単独ユーザ、もしくは100%信頼できるメンバーでのみNucleusを運営している場合は、アップグレードの必要はありません。

また、同様の脆弱性がNP_Mediatocu及びNP_ImprovedMediaに発見されています。NP_Mediatocuについては、複数・単独ユーザでの使用に関わらず、対策版が出るまではアンインストールすることをお勧めします。NP_ImprovedMediaに関しては、単独メンバーでの使用は問題ありませんが、複数メンバーで使用している場合は対策版が出るまでの間はアンインストールをお勧めします。

Nucleus-SQLiteでも、SP3を用意しました。該当する方は、アップグレードをお願いします。]]>
Nucleus https://www.rad51.net/jeans/?itemid=697 Tue, 16 Dec 2008 12:52:07 PST ITEM697_20081216
メインの目次ページで、prevlink, nextlink https://www.rad51.net/jeans/?itemid=698
ただし、<%if(previtem)%>などは使えない。これは、個別アイテムページ及びアーカイブページでのみ使えるようである。なので、次や前のページが無いときにリンクが表示されないようにするには、少し工夫が必要。

JavaScriptを用いれば、こんな風に使える。
<a href="<%nextlink%>" id="nextlink">older items</a>
<a href="<%prevlink%>" id="prevlink">newer items</a>
<script type="text/javascript">
function hide_link(id){
  var element=document.getElementById(id);
  if (element.href=="" || element.href==document.location)
    element.style.display="none";
}
hide_link("nextlink");
hide_link("prevlink");
</script>

NP_ShowBlogsやNP_PageSwitchを使うまでもないような、ちょっとしたページ遷移に便利かも。]]>
Nucleus https://www.rad51.net/jeans/?itemid=698 Tue, 16 Dec 2008 12:07:00 PST ITEM698_20081216
プラグインをコピーして使う https://www.rad51.net/jeans/?itemid=683 Nucleus JPフォーラムでのあるトピックで、一つのプラグインを二つの異なるプラグインとしてインストールできれば、有用であるケースがあることを知った。フォーラムでの結論としては、プラグインのPHPファイル及び下層ディレクトリのファイル名を書き換えてコピーし、クラス名も同じ名で書き換えることで対応できるということになった。

このような対応をした場合、後にプラグインのアップグレードをする時は、コピー元とコピーしたプラグインの両方を変更しなければならない。

masさんがtwitterで、これを解決する方法をつぶやいていたので、少しまとめてここにメモ。

結論としては、NP_Calendar_english.phpを次の内容で作成すれば、それでOK。

<?php

$this->getPlugin('NP_Calendar');
class NP_Calendar_english extends NP_Calendar {
    function getShortName() {
        return 'calendar';
    }
    function install() { }
    function unInstall() { }
}

少し解説。

1)ミソはmasさんのアイデア、『class NP_Calendar_english extends NP_Calendar』。要は、NP_Calendar_englishというクラスが、NP_Calendarと同じ動作をすればよいので、そっくりそのまま継承すればよい。

2)ただし、NP_Calendar_englishクラスの定義の際、NP_Calendarクラスが定義されていないと、fatal errorとなる。そこで必要なのが、『$this->getPlugin('NP_Calendar');』の行。ここで、NP_Calendarオブジェクトを作成している。ちなみに、『$this』はMANAGERオブジェクト。プラグインのPHPファイルは、MANAGER::_loadPlugin()メソッド内で、include()命令により読み込まれているので、『$this』によりMANAGERオブジェクトにアクセス可能。

3)もしプラグインが下層ディレクトリを持っていない場合、上記の1)、2)の実装だけでOKのはず。つまり、
<?php

$this->getPlugin('NP_Calendar');
class NP_Calendar_english extends NP_Calendar {
}
でよい。しかし、下層ディレクトリを持っている場合はこれだけではうまく行かない。今の例だと、日本語版のカレンダープラグインでプラグイン独自の言語ファイルを読み込みもうとするとき、『plugins/calendar_english/』ディレクトリ以下のファイルを利用しようとする。これをごまかすには、getShortName()メソッドをオーバライドして、下層ディレクトリに『plugins/calendar/』が利用されるようにすれば良い。

4)h1028さんのコメントを受けて修正し、install(), uninstall()メソッドをオーバライドして、プラグインのインストール時とアンインストール時に、何も行わないようにした。

補足:上記コードは、もしかしたらPHP4ではうまく働かないかもしれない。その場合は、次のように変更すればうまく行くかも。

<?php

$this->getPlugin('NP_Calendar');
if (!class_exists('NP_Calendar_english')) {
    class NP_Calendar_english extends NP_Calendar {
        function getShortName() {
            return 'calendar';
        }
        function install() { }
        function unInstall() { }
    }
}


(2008-11-18 改定)]]>
Nucleus https://www.rad51.net/jeans/?itemid=683 Sat, 15 Nov 2008 15:34:59 PST ITEM683_20081115
Nucleus 3.31 SP2出ました(EUC-JP版のみ)。 https://www.rad51.net/jeans/?itemid=678 XSS脆弱性を修正した、3.31 SP2が出ました。ただし、脆弱性はEUC-JP版を、インターネットエクスプローラ・バージョン6で用いたときに、出ます。ですので、次の事例に該当する場合は、現在のところバージョンアップする必要はありません。

1)UTF8版、および英語版を用いている場合
2)EUC-JP版を、イントラネットなどのローカル環境でのみ使用しており、かつ、閲覧している全員がIE7もしくはFireFoxなどのブラウザのみを用いている場合。

EUC-JPを用いているけれども、管理にはIE6以外のブラウザ(IE7やFireFox)を用いているケースでは、危険性は少ないです。ただし、IE6で閲覧した第三者に意図しないサイト表示がされたりする可能性があります。こういったケースでも必ずバージョンアップを行ってください。

なお、SQLite版についてもSP2を用意しましたので、EUC-JPで用いている方はバージョンアップをお願いします。]]>
Nucleus https://www.rad51.net/jeans/?itemid=678 Sat, 27 Sep 2008 01:40:13 PDT ITEM678_20080927
Nucleusの乗っ取り、その1:スキン変数を自由にマネージする。 https://www.rad51.net/jeans/?itemid=664
ある方とPMでやりとりしていたときに思いついたプラグインがあって、眠らせておくにはもったいないアイデアなのでここで紹介。

Nucleusのスキン変数は、コアで用意されているものと、プラグインで用意されているものと2種類ある。プラグインで用意できるものは、プラグイン名から『NP_』を除いた残りの名前だけ。

また、コアで用意されているものをプラグインでオーバライドすることはできない。例えば、NP_blog という名のプラグインを作成しても、スキン変数<%blog%>ではコアのコードが呼び出されてしまう。

コアの変更はせずに、これらを何とかしようという話。

次のプラグインは、スキン及びアイテム中で、『<% %>』以外に、『[% %]』もNucleus変数として使えるようにするもの。

<?php
class NP_ConvertTags extends NucleusPlugin {
   function getName() { return preg_replace('/\.php$/','',basename(__FILE__)); }
   function getMinNucleusVersion() { return 330; }
   function getAuthor()  { return 'Katsumi'; }
   function getVersion() { return '0.1'; }
   function getURL() {return 'http://japan.nucleuscms.org/wiki/plugins:authors:katsumi';}
   function getDescription() { return $this->getName().' plugin'; }
   function supportsFeature($what) { return ($what=='SqlTablePrefix')?1:0; }
   function getEventList() { return array('PreSkinParse','PreItem'); }
   function doSkinVar($skinType,$type,$p2='') {
      switch($type){
      case 'parsedinclude':
      default:
         return $this->parse_parsedinclude($p2);
      }
   }
   function event_PreItem(&$data) {
      // Convert
      $item=&$data['item'];
      $item->body=$this->convert($item->body);
      $item->more=$this->convert($item->more);
   }
   var $handler,$parser;
   function event_PreSkinParse(&$data) {      
      // Convert
      $contents=&$data['contents'];
      $contents=$this->convert($contents);
      // Set handler and parser
      $skin=&$data['skin'];
      $type=&$data['type'];
      $actions = $skin->getAllowedActionsForType($type);
      $this->handler =& new ACTIONS($type, $skin);
      $this->parser =& new PARSER($actions, $this->handler);
      $this->handler->setParser($this->parser);
      $this->handler->setSkin($skin);
   }
   function parse_parsedinclude($filename) {
      // check current level
      if ($this->handler->level > 3) return;   // max. depth reached (avoid endless loop)
      $filename = $this->handler->getIncludeFileName($filename);
      if (!file_exists($filename)) return '';

      $fsize = filesize($filename);

      // nothing to include
      if ($fsize <= 0)
         return;

      $this->handler->level++;

      // read file
      $fd = fopen ($filename, 'r');
      $contents = fread ($fd, $fsize);
      fclose ($fd);

      // convert
      $contents=$this->convert($contents);

      // parse file contents
      $this->parser->parse($contents);

      $this->handler->level--;
   }
   var $search=array('/\[%([\s\S]*?)%\]/');
   function convert(&$text) {
      $temp=preg_replace($this->search,'<%$1%>',$text);
      return preg_replace('/<%parsedinclude\(([^\(]*)\)%>/','<%ConvertTags(parsedinclude,$1)%>',$temp);
   }
}
?>


アイテムに関しては、event_PreItem()で処理する、簡単な動作。

スキンに関しては、event_PreSkinParse()で処理する(3.22以降が必要)。ただし、<%parsedinclude%>の際にはこのイベントが呼ばれないので、表題のNucleusの乗っ取り『スキン変数を自由にマネージする』が必要になる。

1)まず<%parsedinclude%>をオーバライドするために、このプラグインのコード中、convert()メソッドの2行めで、<%parsedinclude(xxx)%>を<%ConverTags(parsedinclude,xxx)%>に書き換えている。

2)結果としてdoSkinVar()メソッドが呼び出されるから、1番目の引数に『parsedinclude』を取るものはparse_parsedinclude()メソッドにリダイレクトする。

3)parse_parsedinclude()メソッド中に、機能拡張したコードを記述する。

同じ原理で、すべてのスキン変数を管理することが可能のはず。]]>
Nucleus https://www.rad51.net/jeans/?itemid=664 Fri, 11 Jul 2008 12:52:06 PDT ITEM664_20080711