expresser
Version:
A ready-to-use platform for Node.js web apps, built on top of Express.
501 lines (243 loc) • 20.6 kB
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)|<)((\%2F)|\/)*[a-z0-9\%]+((\%3E)|>)/i</span>, <span class="hljs-comment"># simple XSS</span>
<span class="hljs-regexp">/((\%3C)|<)((\%69)|i|(\%49))((\%6D)|m|(\%4D))((\%67)|g|(\%47))[^\n]+((\%3E)|>)/i</span>, <span class="hljs-comment"># img src XSS</span>
<span class="hljs-regexp">/((\%3C)|<)[^\n]+((\%3E)|>)/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> =></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> =></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> =></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> =></span>
p = patterns[<span class="hljs-built_in">module</span>].length - <span class="hljs-number">1</span>
<span class="hljs-keyword">while</span> p >= <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> =></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> =></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> =></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> =></span>
p = patterns[<span class="hljs-built_in">module</span>].length - <span class="hljs-number">1</span>
<span class="hljs-keyword">while</span> p >= <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> =></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"> =></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> =></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 < 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 >= 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> =></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> =></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> =></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> = -></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>