UNPKG

expresser

Version:

A ready-to-use platform for Node.js web apps, built on top of Express.

501 lines (243 loc) 20.6 kB
<!DOCTYPE html> <html> <head> <title>firewall.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="public/stylesheets/normalize.css" /> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div class="container"> <div class="page"> <div class="header"> <h1>firewall.coffee</h1> <div class="toc"> <h3>Table of Contents</h3> <ol> <li> <a class="source" href="index.html"> index.coffee </a> </li> <li> <a class="source" href="app.html"> app.coffee </a> </li> <li> <a class="source" href="cron.html"> cron.coffee </a> </li> <li> <a class="source" href="database.html"> database.coffee </a> </li> <li> <a class="source" href="downloader.html"> downloader.coffee </a> </li> <li> <a class="source" href="events.html"> events.coffee </a> </li> <li> <a class="source" href="firewall.html"> firewall.coffee </a> </li> <li> <a class="source" href="imaging.html"> imaging.coffee </a> </li> <li> <a class="source" href="logger.html"> logger.coffee </a> </li> <li> <a class="source" href="mailer.html"> mailer.coffee </a> </li> <li> <a class="source" href="settings.html"> settings.coffee </a> </li> <li> <a class="source" href="sockets.html"> sockets.coffee </a> </li> <li> <a class="source" href="utils.html"> utils.coffee </a> </li> </ol> </div> </div> <h2 id="expresser-firewall">EXPRESSER FIREWALL</h2> <p>Firewall to protect the server against well known HTTP and socket attacks. ATTENTION! The Firewall module is started automatically by the App module. If you wish to disable it, set <code>Settings.firewall.enabled</code> to false. <!-- @see Settings.firewall --></p> <div class='highlight'><pre><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Firewall</span></span> async = <span class="hljs-built_in">require</span> <span class="hljs-string">"async"</span> lodash = <span class="hljs-built_in">require</span> <span class="hljs-string">"lodash"</span> logger = <span class="hljs-built_in">require</span> <span class="hljs-string">"./logger.coffee"</span> moment = <span class="hljs-built_in">require</span> <span class="hljs-string">"moment"</span> settings = <span class="hljs-built_in">require</span> <span class="hljs-string">"./settings.coffee"</span> sockets = <span class="hljs-string">"./sockets.coffee"</span> util = <span class="hljs-built_in">require</span> <span class="hljs-string">"util"</span> utils = <span class="hljs-built_in">require</span> <span class="hljs-string">"./utils.coffee"</span></pre></div> <p>Holds a collection of blacklisted IPs.</p> <div class='highlight'><pre> <span class="hljs-attribute">blacklist</span>: <span class="hljs-literal">null</span></pre></div> <h2 id="protection-patterns">PROTECTION PATTERNS</h2> <div class='highlight'><pre> patterns = {} patterns.lfi = [<span class="hljs-regexp">/\.\.\//</span>] <span class="hljs-comment"># root path match</span> patterns.sql = [<span class="hljs-regexp">/((\%3D)|(=))[^\n]*((\%27)|(\')|(\-\-)|(\%3B)|(;))/i</span>, <span class="hljs-comment"># SQL meta chars</span> <span class="hljs-regexp">/\w*((\%27)|(\'))((\%6F)|o|(\%4F))((\%72)|r|(\%52))/i</span>, <span class="hljs-comment"># simple SQL injection</span> <span class="hljs-regexp">/((\%27)|(\'))union/i</span>, <span class="hljs-comment"># union SQL injection</span> <span class="hljs-regexp">/exec(\s|\+)+(s|x)p\w+/i</span>, <span class="hljs-comment"># MSSQL specific injection</span> <span class="hljs-regexp">/UNION(?:\s+ALL)?\s+SELECT/i</span>] <span class="hljs-comment"># union select SQL injection</span> patterns.xss = [<span class="hljs-regexp">/((\%3C)|&lt;)((\%2F)|\/)*[a-z0-9\%]+((\%3E)|&gt;)/i</span>, <span class="hljs-comment"># simple XSS</span> <span class="hljs-regexp">/((\%3C)|&lt;)((\%69)|i|(\%49))((\%6D)|m|(\%4D))((\%67)|g|(\%47))[^\n]+((\%3E)|&gt;)/i</span>, <span class="hljs-comment"># img src XSS</span> <span class="hljs-regexp">/((\%3C)|&lt;)[^\n]+((\%3E)|&gt;)/i</span>] <span class="hljs-comment"># all other XSS</span></pre></div> <h2 id="init">INIT</h2> <p>Init the firewall. This must be called AFTER the web app has started. @param [Object] Firewall init options. @option options [Object] server The Express server object to bind to.</p> <div class='highlight'><pre> <span class="hljs-attribute">init</span>: <span class="hljs-function"><span class="hljs-params">(options, callback)</span> =&gt;</span> options = {<span class="hljs-attribute">server</span>: options} <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> options.server? env = process.env</pre></div> <p>Server is mandatory!</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> options.server? logger.error <span class="hljs-string">"Firewall.init"</span>, <span class="hljs-string">"App server is invalid. Abort!"</span> <span class="hljs-keyword">return</span></pre></div> <p>Bind HTTP protection.</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> settings.firewall.httpPatterns <span class="hljs-keyword">isnt</span> <span class="hljs-string">""</span> <span class="hljs-keyword">and</span> env.NODE_ENV <span class="hljs-keyword">isnt</span> <span class="hljs-string">"test"</span> options.server.use <span class="hljs-property">@checkHttpRequest</span> logger.info <span class="hljs-string">"Firewall.init"</span>, <span class="hljs-string">"Protect HTTP requests."</span></pre></div> <p>Bind sockets protection.</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> settings.firewall.socketPatterns <span class="hljs-keyword">isnt</span> <span class="hljs-string">""</span> <span class="hljs-keyword">and</span> env.NODE_ENV <span class="hljs-keyword">isnt</span> <span class="hljs-string">"test"</span> logger.info <span class="hljs-string">"Firewall.init"</span>, <span class="hljs-string">"Protect Socket requests."</span> <span class="hljs-property">@blacklist</span> = {}</pre></div> <h2 id="http-protection">HTTP PROTECTION</h2> <p>Check HTTP requests against common web attacks.</p> <div class='highlight'><pre> <span class="hljs-attribute">checkHttpRequest</span>: <span class="hljs-function"><span class="hljs-params">(req, res, next)</span> =&gt;</span> ip = utils.getClientIP req</pre></div> <p>If IP is blaclisted, end the request immediatelly.</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> <span class="hljs-property">@checkBlacklist</span> ip <span class="hljs-property">@sendAccessDenied</span> res, <span class="hljs-string">"Blacklisted"</span></pre></div> <p>Helper method to check HTTP patterns.</p> <div class='highlight'><pre> <span class="hljs-function"><span class="hljs-title">check</span> = <span class="hljs-params">(p)</span> =&gt;</span> <span class="hljs-property">@checkHttpPattern</span> p, req, res</pre></div> <p>Set valid and checl all enabled patterns.</p> <div class='highlight'><pre> valid = <span class="hljs-literal">true</span> enabledPatterns = settings.firewall.httpPatterns.split <span class="hljs-string">","</span> <span class="hljs-keyword">for</span> pattern <span class="hljs-keyword">in</span> enabledPatterns valid = <span class="hljs-literal">false</span> <span class="hljs-keyword">if</span> check(pattern)</pre></div> <p>Only proceed if request is valid.</p> <div class='highlight'><pre> next() <span class="hljs-keyword">if</span> valid</pre></div> <p>Test the request against the enabled protection patterns.</p> <div class='highlight'><pre> <span class="hljs-attribute">checkHttpPattern</span>: <span class="hljs-function"><span class="hljs-params">(<span class="hljs-built_in">module</span>, req, res)</span> =&gt;</span> p = patterns[<span class="hljs-built_in">module</span>].length - <span class="hljs-number">1</span> <span class="hljs-keyword">while</span> p &gt;= <span class="hljs-number">0</span> <span class="hljs-keyword">if</span> patterns[<span class="hljs-built_in">module</span>][p].test req.url <span class="hljs-property">@handleHttpAttack</span> <span class="hljs-string">"<span class="hljs-subst">#{<span class="hljs-built_in">module</span>}</span>"</span>, p, req, res <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span> --p <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span></pre></div> <p>Handle attacks.</p> <div class='highlight'><pre> <span class="hljs-attribute">handleHttpAttack</span>: <span class="hljs-function"><span class="hljs-params">(<span class="hljs-built_in">module</span>, pattern, req, res)</span> =&gt;</span> ip = utils.getClientIP req <span class="hljs-property">@logAttack</span> <span class="hljs-built_in">module</span>, pattern, req.url, ip <span class="hljs-property">@sendAccessDenied</span> res</pre></div> <h2 id="sockets-protection">SOCKETS PROTECTION</h2> <p>Check Socket requests against common web attacks.</p> <div class='highlight'><pre> <span class="hljs-attribute">checkSocketRequest</span>: <span class="hljs-function"><span class="hljs-params">(socket, message, next)</span> =&gt;</span> ip = utils.getClientIP req</pre></div> <p>If IP is blaclisted, end the request immediatelly.</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> <span class="hljs-property">@checkBlacklist</span> ip <span class="hljs-property">@sendAccessDenied</span> socket, <span class="hljs-string">"Blacklisted"</span></pre></div> <p>Helper method to check socket patterns.</p> <div class='highlight'><pre> <span class="hljs-function"><span class="hljs-title">check</span> = <span class="hljs-params">(p)</span> =&gt;</span> <span class="hljs-property">@checkSocketPattern</span> p, socket, util.inspect(message)</pre></div> <p>Set valid and checl all enabled patterns.</p> <div class='highlight'><pre> valid = <span class="hljs-literal">true</span> enabledPatterns = settings.firewall.socketPatterns.split <span class="hljs-string">","</span> <span class="hljs-keyword">for</span> pattern <span class="hljs-keyword">in</span> enabledPatterns valid = <span class="hljs-literal">false</span> <span class="hljs-keyword">if</span> check(pattern)</pre></div> <p>Only proceed if request is valid.</p> <div class='highlight'><pre> next() <span class="hljs-keyword">if</span> valid</pre></div> <p>Test the request against the enabled protection patterns.</p> <div class='highlight'><pre> <span class="hljs-attribute">checkSocketPattern</span>: <span class="hljs-function"><span class="hljs-params">(<span class="hljs-built_in">module</span>, socket, message)</span> =&gt;</span> p = patterns[<span class="hljs-built_in">module</span>].length - <span class="hljs-number">1</span> <span class="hljs-keyword">while</span> p &gt;= <span class="hljs-number">0</span> <span class="hljs-keyword">if</span> patterns[<span class="hljs-built_in">module</span>][p].test message <span class="hljs-property">@handleSocketAttack</span> <span class="hljs-string">"<span class="hljs-subst">#{<span class="hljs-built_in">module</span>}</span>"</span>, p, socket, message <span class="hljs-keyword">return</span> --p</pre></div> <p>Handle attacks.</p> <div class='highlight'><pre> <span class="hljs-attribute">handleSocketAttack</span>: <span class="hljs-function"><span class="hljs-params">(<span class="hljs-built_in">module</span>, pattern, socket, message)</span> =&gt;</span> ip = utils.getClientIP socket <span class="hljs-property">@logAttack</span> <span class="hljs-built_in">module</span>, pattern, message, ip <span class="hljs-property">@sendAccessDenied</span> socket</pre></div> <h2 id="blacklist-methods">BLACKLIST METHODS</h2> <p>Reset the blacklist object.</p> <div class='highlight'><pre> <span class="hljs-attribute">clearBlacklist</span>:<span class="hljs-function"> =&gt;</span> <span class="hljs-property">@blacklist</span> = {}</pre></div> <p>Check if the specified IP is blacklisted.</p> <div class='highlight'><pre> <span class="hljs-attribute">checkBlacklist</span>: <span class="hljs-function"><span class="hljs-params">(ip)</span> =&gt;</span> bl = <span class="hljs-property">@blacklist</span>[ip] <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> bl? <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span></pre></div> <p>Check if record has expired.</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> bl.expires &lt; moment() <span class="hljs-keyword">delete</span> <span class="hljs-property">@blacklist</span>[ip] <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span></pre></div> <p>Increase the blacklist count, and increase the expiry date in case it has reached the max retries.</p> <div class='highlight'><pre> bl.count = bl.count + <span class="hljs-number">1</span> <span class="hljs-keyword">if</span> bl.count &gt;= settings.firewall.blacklistMaxRetries bl.expires = moment().add <span class="hljs-string">"s"</span>, settings.firewall.blacklistLongExpires <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span></pre></div> <p>Add the specified IP to the blacklist.</p> <div class='highlight'><pre> <span class="hljs-attribute">addToBlacklist</span>: <span class="hljs-function"><span class="hljs-params">(ip)</span> =&gt;</span> bl = <span class="hljs-property">@blacklist</span>[ip] bl = {} <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> bl?</pre></div> <p>Set blacklist object.</p> <div class='highlight'><pre> bl.expires = moment().add <span class="hljs-string">"s"</span>, settings.firewall.blacklistExpires bl.count = <span class="hljs-number">1</span> <span class="hljs-property">@blacklist</span>[ip] = bl</pre></div> <h2 id="helper-methods">HELPER METHODS</h2> <p>Send an access denied and end the request in case it wasn’t authorized.</p> <div class='highlight'><pre> <span class="hljs-attribute">sendAccessDenied</span>: <span class="hljs-function"><span class="hljs-params">(obj, message)</span> =&gt;</span> message = <span class="hljs-string">"Access denied"</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> message?</pre></div> <p>Set status and message.</p> <div class='highlight'><pre> obj.status(<span class="hljs-number">403</span>) <span class="hljs-keyword">if</span> obj.status? <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> obj.headerSent obj.send(message) <span class="hljs-keyword">if</span> obj.send?</pre></div> <p>Disconnect or end?</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> obj.disconnect? <span class="hljs-keyword">return</span> obj.disconnect() <span class="hljs-keyword">else</span> <span class="hljs-keyword">return</span> obj.end()</pre></div> <p>Log attacks.</p> <div class='highlight'><pre> <span class="hljs-attribute">logAttack</span>: <span class="hljs-function"><span class="hljs-params">(<span class="hljs-built_in">module</span>, pattern, resource, ip)</span> =&gt;</span> logger.warn <span class="hljs-string">"ATTACK DETECTED!"</span>, <span class="hljs-built_in">module</span>, pattern, resource, <span class="hljs-string">"From <span class="hljs-subst">#{ip}</span>"</span></pre></div> <h2 id="singleton-implementation">Singleton implementation</h2> <div class='highlight'><pre>Firewall.<span class="hljs-function"><span class="hljs-title">getInstance</span> = -&gt;</span> <span class="hljs-property">@instance</span> = <span class="hljs-keyword">new</span> Firewall() <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> <span class="hljs-property">@instance</span>? <span class="hljs-keyword">return</span> <span class="hljs-property">@instance</span> <span class="hljs-built_in">module</span>.<span class="hljs-built_in">exports</span> = <span class="hljs-built_in">exports</span> = Firewall.getInstance()</pre></div> <div class="fleur">h</div> </div> </div> </body> </html>