<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>security — jd:/dev/blog</title><description>Posts tagged &quot;security&quot; on jd:/dev/blog.</description><link>https://julien.danjou.info/</link><item><title>Security Starts Where Convenience Ends</title><link>https://julien.danjou.info/blog/security-theater-is-not-security/</link><guid isPermaLink="true">https://julien.danjou.info/blog/security-theater-is-not-security/</guid><description>The alarming state of security in too many tech companies</description><pubDate>Tue, 27 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Over the past quarter, I’ve had conversations with a handful of engineers working at French software companies — from early-stage startups to more established players. Companies with thousands of users and millions of euros of revenue.&lt;/p&gt;
&lt;p&gt;During the conversation, what struck me wasn’t what they were building or how they scaled. It was how little attention and seriousness many of them gave to security.&lt;/p&gt;
&lt;p&gt;Some of these companies handle critical user data. Others operate infrastructure that powers thousands of customers. Yet, their security posture often amounts to… vibes. A bit of MFA here. A few random VPNs there. But very little that would pass as security maturity by any professional standard.&lt;/p&gt;
&lt;p&gt;And yes — I get it. Security is not easy. It’s thankless. It doesn’t generate revenue. But here’s the deal: ignoring it isn’t neutral. It’s dangerous.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://julien.danjou.info/images/blog/b22f03ac-b154-4fc7-9883-19a60340d81c_1376x864.png&quot; alt=&quot;Illustration of poor security posture in tech companies&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;What’s going wrong?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;From what I’ve seen and heard:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;No MDM (Mobile Device Management):&lt;/strong&gt; Engineers using unmonitored laptops, often their own machines, with no control over OS updates, disk encryption, or even if a password is required. The reason is sometimes that engineers are doing a heavy push back on this for convenience and have too much weight in the decision making process for security, without having a clue.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;No endpoint visibility:&lt;/strong&gt; If a machine is compromised, there’s no way to know. Worse, there’s no way to &lt;em&gt;do anything about it&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;No SOC 2, no ISO 27001, not even a roadmap:&lt;/strong&gt; These aren’t magic bullets, but they’re a minimum bar—a starting point. Yet many companies either dismiss them or postpone them indefinitely.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Weak privilege separation:&lt;/strong&gt; Developers with production access “just in case.” CI pipelines that can destroy environments. You get the picture.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This isn’t just a case of companies not being “mature enough.” This is willful neglect disguised as pragmatism.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://julien.danjou.info/images/blog/7e6bd99f-61a8-46b5-899a-b6299113c982_699x480.png&quot; alt=&quot;Illustration of developers resisting security measures for convenience&quot; /&gt;&lt;/p&gt;
&lt;p&gt;One of the reasons (and &lt;a href=&quot;https://julien.danjou.info/blog/why-engineers-shouldnt-decide-your&quot;&gt;why engineers shouldn&apos;t always decide your strategy&lt;/a&gt;): developers often act like divas. Many of them refuse to make even minor trade-offs in convenience for the sake of better security. They don’t want to lose their admin rights, install an MDM agent, or be told they can’t SSH into prod “just in case.” Security? That’s someone else’s problem—until it’s not. The bigger issue is that in many early-stage or engineering-led companies, devs hold disproportionate decision-making power, and there’s no one truly responsible for security. Without pushback, security becomes optional. This isn’t about lack of maturity. It’s about a complete lack of incentives, accountability, and understanding.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;“We’re not a target” is a myth&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Many of these teams believe they’re too small or irrelevant to be attacked. That might be true—until it’s not.&lt;/p&gt;
&lt;p&gt;In France, &lt;a href=&quot;https://www.france24.com/en/france/20250513-armed-gang-tries-to-kidnap-crypto-ceo-s-daughter-grandson-in-central-paris&quot;&gt;we’ve recently seen crypto entrepreneurs attacked physically&lt;/a&gt;. That’s one end of the spectrum. But digital attacks? They don’t need to be targeted. They can be opportunistic. You leave a port open, someone finds it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://julien.danjou.info/images/blog/e3040ae4-cdf9-41db-aeec-5b69b9e508f6_709x723.webp&quot; alt=&quot;Screenshot of article about armed gang attacking crypto entrepreneurs in Paris&quot; /&gt;
&lt;em&gt;Original Article&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Security hygiene isn’t about paranoia. It’s about respecting your customers, your users, and your own future.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;What should change?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;I’m not a security expert. I run a bootstrapped software company, and I’m just one of the gears in our security. But here’s what I’d like to see as a baseline across every SaaS company:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;MDM. For every laptop.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Disk encryption. Mandatory.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Admin access? Logged. Monitored. Reviewed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Least privilege policies by default.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CI/CD pipelines with auditable change control.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Security reviews baked into product releases.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A roadmap toward certifications like SOC 2 or ISO 27001.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not because they’re trendy, but because they force discipline.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Why I’m writing this&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Because I’m genuinely worried. I think we’re going to see more breaches, more leaks, more “oops we exposed production DBs for a month” stories. And when that happens, saying “well it was complicated” won’t cut it.&lt;/p&gt;
&lt;p&gt;Security is part of the job. It’s not an add-on. It’s not the CISO’s problem. It’s yours. It’s mine. It’s everyone’s.&lt;/p&gt;
&lt;p&gt;Let’s raise the bar.&lt;/p&gt;
</content:encoded><category>security</category></item><item><title>Extending Swift with middleware: example with ClamAV</title><link>https://julien.danjou.info/blog/extending-swift-with-a-middleware-clamav/</link><guid isPermaLink="true">https://julien.danjou.info/blog/extending-swift-with-a-middleware-clamav/</guid><description>In this article, I&apos;m going to explain you how you can extend Swift, the OpenStack Object Storage project, so it performs extra action on files at upload or at download time.</description><pubDate>Tue, 22 Jan 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In this article, I&apos;m going to explain you how you can extend &lt;a href=&quot;http://launchpad.net/swift&quot;&gt;Swift&lt;/a&gt;, the &lt;a href=&quot;http://openstack.org&quot;&gt;OpenStack&lt;/a&gt; Object Storage project, so it performs extra action on files at upload or at download time.&lt;/p&gt;
&lt;p&gt;We&apos;re going to build an anti-virus filter inside Swift. The goal is to refuse uploaded data if they contain a virus. To help us with virus analyses, we&apos;ll use &lt;a href=&quot;http://www.clamav.net&quot;&gt;ClamAV&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;WSGI, paste and middleware&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://julien.danjou.info/content/images/03/lolcat-tube.jpg&quot; alt=&quot;lolcat-tube&quot; /&gt;&lt;/p&gt;
&lt;p&gt;To do our content analysis, the best place to hook in the Swift architecture is at the beginning of every request, on &lt;strong&gt;swift-proxy&lt;/strong&gt;, before the file is actually stored on the cluster. Swift proxy uses, like many other OpenStack projects, &lt;a href=&quot;https://pypi.python.org/pypi/Paste&quot;&gt;paste&lt;/a&gt; to build his HTTP architecture.&lt;/p&gt;
&lt;p&gt;Paste uses WSGI and provides an architecture based on a pipeline. The pipeline is composed of a succession of middleware, ending with one application. Each middleware has the chance to look at the request or at the response, can modify it, and then pass it to the following middleware. The latest component of the pipeline is the real application, and in this case, the Swift proxy server.&lt;/p&gt;
&lt;p&gt;If you&apos;ve already deployed Swift, you encountered a default pipeline in the &lt;em&gt;swift-proxy.conf&lt;/em&gt; configuration file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[pipeline:main]
pipeline = catch_errors healthcheck cache ratelimit tempauth proxy-logging proxy-server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a really basic pipeline with a few middleware. The first one catches error, the second one is in charge to return &lt;em&gt;200 OK&lt;/em&gt; response if you send a &lt;code&gt;GET /healthcheck&lt;/code&gt; request on your proxy server. The third one is in charge of caching, the fourth one is used for rate limiting, the fifth for authentication, the sixth one for logging, and the final one is the actual proxy server, in charge of proxying the request to the account, container, or object servers (the others components of Swift). Of course, we could remove or add any of the middleware here at our convenience.&lt;/p&gt;
&lt;p&gt;Be aware that the order matters: for example, if you put &lt;em&gt;healthcheck&lt;/em&gt; after &lt;em&gt;tempauth&lt;/em&gt;, you won&apos;t be able to access the &lt;em&gt;/healthcheck&lt;/em&gt; URL without being authenticated!&lt;/p&gt;
&lt;h2&gt;ClamAV&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://julien.danjou.info/content/images/03/clamav.png&quot; alt=&quot;clamav&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If you don&apos;t know &lt;a href=&quot;http://clamav.org&quot;&gt;ClamAV&lt;/a&gt;, it&apos;s an antivirus engine designed for detecting trojans, viruses, malware and other malicious threats. Wwe&apos;re going to use it to scan every incoming file. To build the middleware, we&apos;ll use the Python binding &lt;a href=&quot;http://pypi.python.org/pypi/clamd&quot;&gt;pyclamd&lt;/a&gt;. The API is quite simple, see:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import pyclamd
&amp;gt;&amp;gt;&amp;gt; pyclamd.init_unix_socket(&apos;/var/run/clamav/clamd.ctl&apos;)
&amp;gt;&amp;gt;&amp;gt; print pyclamd.scan_stream(pyclamd.EICAR)
{&apos;stream&apos;: &apos;Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68)&apos;}
&amp;gt;&amp;gt;&amp;gt; print pyclamd.scan_stream(&quot;safe!&quot;)
None
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Anatomy of a WSGI middleware&lt;/h2&gt;
&lt;p&gt;Your WSGI middleware should consist of a callable object. Usually this is done with a class implementing the &lt;em&gt;__call__&lt;/em&gt; method. Here&apos;s a basic boilerplate:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class SwiftClamavMiddleware(object):
    &quot;&quot;&quot;Middleware doing virus scan for Swift.&quot;&quot;&quot;

    def __init__(self, app, conf):
        # app is the final application
        self.app = app

    def __call__(self, env, start_response):
        return self.app(env, start_response)

def filter_factory(global_conf, **local_conf):
    conf = global_conf.copy()
    conf.update(local_conf)

    def clamav_filter(app):
        return SwiftClamavMiddleware(app, conf)
    return clamav_filter
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&apos;m not going to expand more on why this is built this way, but if you want to have more info on this kind of filter middleware, you can read &lt;a href=&quot;http://pythonpaste.org/deploy/#paste-filter-factory&quot;&gt;their documentation on Paste&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This middleware will just do nothing as it is. It&apos;s going to simply pass all requests it receives to the final application, and returns the result.&lt;/p&gt;
&lt;h2&gt;Testing our basic middleware&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://julien.danjou.info/content/images/03/lolcat-testing.jpg&quot; alt=&quot;lolcat-testing&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now is a really good time to add unit tests. I hope you didn&apos;t think we were going to write code without some tests, right? It&apos;s really easy to test a middleware, as we&apos;re going to use &lt;a href=&quot;http://webob.org/&quot;&gt;WebOb&lt;/a&gt; for that.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import unittest
from webob import Request

class FakeApp(object):
    def __call__(self, env, start_response):
        return Response(body=&quot;FAKE APP&quot;)(env, start_response)

class TestSwiftClamavMiddleware(unittest.TestCase):

    def setUp(self):
        self.app = SwiftClamavMiddleware(FakeApp(), {})

    def test_simple_request(self):
        resp = Request.blank(&apos;/&apos;,
                             environ={
                                 &apos;REQUEST_METHOD&apos;: &apos;GET&apos;,
                             }).get_response(self.app)
        self.assertEqual(resp.body, &quot;FAKE APP&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We create a FakeApp class, that represents a fake WSGI application. You could also use a real application, or write a fake application looking like the one you want to test. It&apos;ll require more time, but your tests will be closer to the reality.&lt;/p&gt;
&lt;p&gt;Here we write the simplest test we can for our middleware. We&apos;re just sending a &lt;em&gt;GET /&lt;/em&gt; request to it, so it passes the request to the final application and returns the result. It is transparent, it does nothing.&lt;/p&gt;
&lt;p&gt;Now, with that solid base we&apos;ll able to add more features and test these features incrementally.&lt;/p&gt;
&lt;h2&gt;Plugging ClamAV in&lt;/h2&gt;
&lt;p&gt;With our base ready, we can start thinking about how to plug ClamAV in. What we want to check here, is the content of the file when it&apos;s uploaded. If we refer to the &lt;a href=&quot;http://docs.openstack.org/api/openstack-object-storage/1.0/content/&quot;&gt;OpenStack object storage API&lt;/a&gt;, a file upload is done via a &lt;em&gt;PUT&lt;/em&gt; request, so we&apos;re going to limit the check to that kind of requests. Obviously, more checks could be added, but we&apos;ll keep things simple here for the sake of comprehensibility.&lt;/p&gt;
&lt;p&gt;With WSGI, the content of the request is available in &lt;code&gt;env[&apos;wsgi.input&apos;]&lt;/code&gt; as an object implementing a file interface. We&apos;ll scan that stream with ClamAV to check for viruses.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import pyclamd
from webob import Response

class SwiftClamavMiddleware(object):
    &quot;&quot;&quot;Middleware doing virus scan for Swift.&quot;&quot;&quot;

    def __init__(self, app, conf):
        pyclamd.init_unix_socket(&apos;/var/run/clamav/clamd.ctl&apos;)
        # app is the final application
        self.app = app

    def __call__(self, env, start_response):
        if env[&apos;REQUEST_METHOD&apos;] == &quot;PUT&quot;:
            # We have to read the whole content in memory because pyclamd
            # forces us to, but this is a bad idea if the file is huge.
            scan = pyclamd.scan_stream(env[&apos;wsgi.input&apos;].read())
            if scan:
                return Response(status=403,
                                body=&quot;Virus %s detected&quot; % scan[&apos;stream&apos;],
                                content_type=&quot;text/plain&quot;)(env, start_response)
        return self.app(env, start_response)

def filter_factory(global_conf, **local_conf):
    conf = global_conf.copy()
    conf.update(local_conf)

    def clamav_filter(app):
        return SwiftClamavMiddleware(app, conf)
    return clamav_filter
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&apos;s it. We only check for &lt;em&gt;PUT&lt;/em&gt; requests and if there&apos;s a virus in the file, we return a 403 Forbidden error with the name of the detected virus, bypassing entirely the rest of the middleware chain and the application handling.&lt;/p&gt;
&lt;p&gt;Then, we can simply test it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import unittest
from cStringIO import StringIO
from webob import Request, Response

class FakeApp(object):
    def __call__(self, env, start_response):
        return Response(body=&quot;FAKE APP&quot;)(env, start_response)

class TestSwiftClamavMiddleware(unittest.TestCase):
    def setUp(self):
        self.app = SwiftClamavMiddleware(FakeApp(), {})

    def test_put_empty(self):
        resp = Request.blank(&apos;/v1/account/container/object&apos;,
                             environ={
                                 &apos;REQUEST_METHOD&apos;: &apos;PUT&apos;,
                             }).get_response(self.app)
        self.assertEqual(resp.body, &quot;FAKE APP&quot;)

    def test_put_no_virus(self):
        resp = Request.blank(&apos;/v1/account/container/object&apos;,
                             environ={
                                 &apos;REQUEST_METHOD&apos;: &apos;PUT&apos;,
                                 &apos;wsgi.input&apos;: StringIO(&apos;foobar&apos;)
                             }).get_response(self.app)
        self.assertEqual(resp.body, &quot;FAKE APP&quot;)

    def test_put_virus(self):
        resp = Request.blank(&apos;/v1/account/container/object&apos;,
                             environ={
                                 &apos;REQUEST_METHOD&apos;: &apos;PUT&apos;,
                                 &apos;wsgi.input&apos;: StringIO(pyclamd.EICAR)
                             }).get_response(self.app)
        self.assertEqual(resp.status_code, 403)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first test &lt;em&gt;test_put_empty&lt;/em&gt; simulates an empty &lt;em&gt;PUT&lt;/em&gt; request. The second one, &lt;em&gt;test_put_no_virus&lt;/em&gt; simulates a regular &lt;em&gt;PUT&lt;/em&gt; request but with a simple file containing no virus.&lt;/p&gt;
&lt;p&gt;Finally, the third and last test simulates the upload of a virus using the &lt;a href=&quot;http://www.eicar.org/&quot;&gt;EICAR&lt;/a&gt; test file. This is a special test file that is recognized as a virus, even if it&apos;s not real one. Very handy for testing virus detection software!&lt;/p&gt;
&lt;h2&gt;Configuring Swift proxy&lt;/h2&gt;
&lt;p&gt;Our middleware is ready! We can configure Swift&apos;s proxy server to use it. We need to add the following lines to our &lt;em&gt;swift-proxy.conf&lt;/em&gt; to teach it how to load the filter:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[filter:clamav]
paste.filter_factory = swiftclamav:filter_factory
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&apos;ll assume that our Python modules is named &lt;em&gt;swiftclamava&lt;/em&gt; here. Now that we&apos;ve defined our filter and how to load it, we can use it in our pipeline:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[pipeline:main]
pipeline = catch_errors healthcheck cache ratelimit tempauth clamav proxy-logging proxy-server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just before reaching the &lt;em&gt;proxy-server&lt;/em&gt;, and after the user being authenticated, the content will be scanned for viruses. It&apos;s important here to put this after authentication for example, because otherwise we may scan content that will get rejected by the authtemp module, thus scanning for nothing!&lt;/p&gt;
&lt;h2&gt;Beyond scanning&lt;/h2&gt;
&lt;p&gt;And voilà, we now have a simple middleware testing uploaded content and refusing infected files. We could enhance it with various other things, like configuration handling, but I&apos;ll let that as an exercise for the interested readers.&lt;/p&gt;
&lt;p&gt;We didn&apos;t exploited it here, but note that you can also manipulate request headers and modify them if needed. For example, we could have added a header &lt;em&gt;X-Object-Meta-Scanned-By: ClamAV&lt;/em&gt; to indicates that the file has been scanned by ClamAV.&lt;/p&gt;
&lt;p&gt;You should now be able to build your own middleware doing whatever you want with uploaded data. Happy hacking!&lt;/p&gt;
</content:encoded><category>openstack</category><category>security</category></item><item><title>Security bug found in Imlib2</title><link>https://julien.danjou.info/blog/security-bug-found-in-imlib2/</link><guid isPermaLink="true">https://julien.danjou.info/blog/security-bug-found-in-imlib2/</guid><description>Yeah, I&apos;m the proud discover of CVE-2008-5187.</description><pubDate>Sat, 22 Nov 2008 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Yeah, I&apos;m the proud discover of &lt;a href=&quot;http://www.securityfocus.com/bid/32371&quot;&gt;CVE-2008-5187&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It&apos;s my first time, it does mean something to me. ;-)&lt;/p&gt;
</content:encoded><category>security</category><category>x11</category></item><item><title>Kicking out Web spammers with DNSBL</title><link>https://julien.danjou.info/blog/kicking-out-web-spammers-with-dnsbl/</link><guid isPermaLink="true">https://julien.danjou.info/blog/kicking-out-web-spammers-with-dnsbl/</guid><description>Every project has its story. Every war has its winner, and its casualties. They were 20 millions men, fighting for their freedom.  And you&apos;ll never know their story.  Because during last week, I was l</description><pubDate>Mon, 15 Jan 2007 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every project has its story. Every war has its winner, and its casualties. They were 20 millions men, fighting for their freedom.&lt;/p&gt;
&lt;p&gt;And you&apos;ll never know their story.&lt;/p&gt;
&lt;p&gt;Because during last week, I was looking why my Web server was so heavily loaded. And I discovered that my blog was attacked by spammers trying to post comments. They were stopped by a great plug-in named &lt;em&gt;spamplemousse&lt;/em&gt;, which use spam keywords and DNSBL to drop spam comments. However, this plug-in is written in PHP, like the rest of my blog, so it loads Apache and MySQL in a way that is no more acceptable: the page have still to be rendered for this !@#$ spammers.&lt;/p&gt;
&lt;p&gt;Consequently, I decided to write a Apache 2.x module which will just drop a &lt;em&gt;403 Forbidden&lt;/em&gt; error page in the spammers&apos; head using DNSBL servers. Here it is, and it is called &lt;a href=&quot;https://github.com/jd/mod_defensible&quot;&gt;mod_defensible&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&apos;m using it since 3 days now, and I got some pretty interesting result and less load on my Web server, so &lt;em&gt;c&apos;est tout bon&lt;/em&gt;.&lt;/p&gt;
</content:encoded><category>web</category><category>email</category><category>security</category></item></channel></rss>