【nginx】IfIsEvil

公式サイトより

IfIsEvilを訳してみた
http://wiki.nginx.org/IfIsEvil

Introduction

ifはlocationコンテキストで予期せぬ動作をすることがある、SIGSEGV(セグメンテーション違反)となることも
ただしifの中がreturn…とrewrite…lastのみ利用しているときは100%安全
またランダムで動作することはないため評価をしっかりしておけば利用しても問題は起こらない
また下の例のように、変数の評価(評価用のdirectiveが存在しない)をするときにifを利用を避けられない場合もある

if ($request_method = POST ) {
  return 405;
}
if ($args ~ post=140){
  rewrite ^ http://example.com/ permanent;
}

What to do instead

return…とrewrite…lastの利用を除き、try_filesを利用したほうがよい
また下の例のように、locationへの記載でなく、server contextへの記載に変更できる場合もある
それ以外にも他のモジュールを利用することで回避できることも

location / {
        error_page 418 = @other;
        recursive_error_pages on;
 
        if ($something) {
            return 418;
        }
 
        # some configuration
        ...
}
 
location @other {
    # some other configuration
    ...
}

Examples

IfIsEvilの実例、この動作は仕様でありバグではない

only-one-if

location /only-one-if {
    set $true 1;

    if ($true) {
        add_header X-First 1;
    }

    if ($true) {
        add_header X-Second 2;
    }

    return 204;
}

他のサーバからアクセスすると

### 2つめのヘッダしか付与されない  ###
root@akat:/home/shimizu# curl --dump-header - http://49.212.204.46/only-one-if
HTTP/1.1 204 No Content
Server: nginx/1.2.1
Date: Sun, 07 Sep 2014 10:42:03 GMT
Connection: keep-alive
X-Second: 2

proxy-pass-uri

location /proxy-pass-uri {
    proxy_pass http://www.google.co.jp/;

    set $true 1;

    if ($true) {
        # nothing
    }
}

他のサーバからアクセスすると

### ifを通過後、proxy_passからredirectする ###
root@akat:/home/shimizu# curl --dump-header - http://49.212.204.46/proxy-pass-uri
HTTP/1.1 404 Not Found
Server: nginx/1.2.1
Date: Sun, 07 Sep 2014 11:13:28 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 1439
Connection: keep-alive
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Alternate-Protocol: 80:quic

<!DOCTYPE html>
<html lang=en>
  <meta charset=utf-8>
  <meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
  <title>Error 404 (Not Found)!!1</title>
  <style>
    *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/errors/logo_sm_2.png) no-repeat}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/errors/logo_sm_2_hr.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/errors/logo_sm_2_hr.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/errors/logo_sm_2_hr.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:55px;width:150px}
  </style>
  <a href=//www.google.com/><span id=logo aria-label=Google></span></a>
  <p><b>404.</b> <ins>That’s an error.</ins>
  <p>The requested URL <code>/proxy-pass-uri</code> was not found on this server.  <ins>That’s all we know.</ins>

if-try-files

location /if-try-files {
     try_files  /file  @fallback;

     set $true 1;

     if ($true) {
         # nothing
     }
}

他のサーバからアクセスすると

### try_filesは動作しない ###
root@akat:/home/shimizu# curl --dump-header - http://49.212.204.46/if-try-files
HTTP/1.1 404 Not Found
Server: nginx/1.2.1
Date: Sun, 07 Sep 2014 11:19:02 GMT
Content-Type: text/html
Content-Length: 168
Connection: keep-alive

<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.2.1</center>
</body>
</html>

crash

location /crash {

    set $true 1;

    if ($true) {
        # fastcgi_pass here
        fastcgi_pass  127.0.0.1:9000;
    }

    if ($true) {
        # no handler here
    }
}

他のサーバからアクセスすると

### SIGSEGV(セグメンテーション違反) fastcgi_pass・proxy_passなどは利用しないほうがよい ###
root@akat:/home/shimizu# curl --dump-header - http://49.212.204.46/crash
curl: (52) Empty reply from server

Why this happens and still not fixed

通常のディレクティブは順番に処理されるが、ifディレクティブはrewrite moduleの一部であり、早い段階で評価される。
ifの中にnon-rewriteディレクティブがあると、上のような動作となる
ifの中のnon-rewriteディレクティブを無効にすることが、ただしいfixであるが、多くの設定に影響があるため対応できていない

If you still want to use if

それでもifを利用したい場合は、動作を理解し、ちゃんとテストして利用すること

参考URL

公式サイト-IfIsEvil-
http://wiki.nginx.org/IfIsEvil
nginxのifに要注意
http://www.techscore.com/blog/2012/10/31/nginx%E3%81%AEif%E3%81%AB%E8%A6%81%E6%B3%A8%E6%84%8F/
How nginx “location if” works
http://agentzh.blogspot.jp/2011/03/how-nginx-location-if-works.html

メニューを閉じる