Code Access Securityその5
CASについて分かってきたつもりだったのですが、また分からなくなってしまいました。CASはどのような場合に使えば良いのでしょうか。
例えば、どこかから使えそうなライブラリ(仮にmalicious.dll)を見つけてきたとします。しかし、malicious.dllは悪意のあるアセンブリで、その実装は、
using System; using System.IO; namespace Malicious { public static class Util { public static void HelloWorld () { using (FileStream fs = File.OpenRead(@"c:\windows\win.ini")) { // 悪意のある処理 } Console.WriteLine ("Hello, World"); } } }
Hello, Worldに見せかけてwin.iniにアクセスしています。呼び出す側は勿論、完全に信用出来ないのでファイルアクセス権限を制限することにしました。
using System; using System.Security; using System.Security.Permissions; [assembly: FileIOPermission (SecurityAction.RequestRefuse, Read=@"c:\windows")] static class Program { public static void Main () { Malicious.Util.HelloWorld (); } }
実行すると、HelloWorldがwin.iniにアクセスしようとした瞬間に例外が発生し、悪意のあるコードは実行されずに済みました。めでたし、めでたし。
ところで、上記の例で何故、悪意のあるアクセスが阻まれたかと言うと、File.OpenReadで権限の要求が行われ、コレによって呼び出し元のコールスタックを大元まで検査が走ります。そして、コールスタック上のどこかにFileIOPermission権限を持たない箇所を見つけると例外が発生するのです。
ところが、これには抜け穴があって、
using System; using System.IO; using System.Security; using System.Security.Permissions; namespace Malicious { // Security.Assertを指定すると呼び出し元のコールスタックは検査しない [FileIOPermission (SecurityAction.Assert, Unrestricted = true)] public class Util { public static void HelloWorld () { using (FileStream fs = File.OpenRead(@"c:\windows\win.ini")) { // 悪意のある処理 } Console.WriteLine ("Hello, World"); } } }
悪意のあるコードがSecurityAction.Assertを使うと、その位置でコールスタックの検査を止めてしまいます。これでは呼び出し元で幾ら権限を外しても検査されないので、win.iniにアクセスすることが出来てしまいます。勿論、SecurityAction.Assertは無条件に使えるわけでなく、SecurityPermission権限が必要です(Assertを呼び出すタイミングでコールスタックの検査が走る)。なので、
using System; using System.Security; using System.Security.Policy; using System.Security.Permissions; [assembly:FileIOPermission (SecurityAction.RequestRefuse, Read=@"c:\windows")] [assembly:SecurityPermission (SecurityAction.RequestRefuse, Flags=SecurityPermissionFlag.Assertion)] class Program { public static void Main () { Malicious.Util.HelloWorld (); } }
呼び出し元でSecurityPermissionを落としてやればいいのかと思ったのですが、こうしても悪意のあるコードは普通に呼び出せてしまいます。RequestRefuseやRequestOptionalで権限を落としてもAssertされたら無意味になってしまうのならCASってどーやって使えば良いのだろうか・・・とか考えてしまいました。
単純に私が間違っているだけでAssertを防ぐ方法はあるとは思うのですが・・・