Hardening WordPress Security Using .htaccess

The default .htaccess file created by WordPress during the installation process on Apache contains only the basic directives needed for WordPress to function. Additional directives may be included in .htaccess to further secure and harden WordPress from common attacks. Many of the below suggestions are not specific to WordPress so they may be used to increase the security posture of any site served by Apache. Other directives are also included below to reduce bandwidth consumption and improve site performance through HTTP compression and browser caching.

The following examples and suggestions assume a single WordPress installation in the document root (not installed in a sub-directory). It also assumes the typical user with a modern browser. If you have a large userbase of early generation browsers, then please adjust accordingly. Remember, defense in depth, so combine these methods with others to create a balance of security and usability.

Prevent Directory Listings

The first directive in the file disables directory listings. If a user attempts to browse a directory, Apache won’t serve a listing of all subdirectories or files at that location. This limits exposure of the underlying file system structure and content.

Options All -Indexes

Hide Apache Version

The next directive instructs Apache to not display its version information on server generated error pages, listings, etc. If a bad actor is scanning for a version of Apache with a known vulnerability, this makes it slightly more difficult to fingerprint.

ServerSignature Off

Limit Allowed HTTP Request Methods

Allowed request methods are limited to POST, GET, HEAD, and OPTIONS. If the site is using other methods, then adjust the list accordingly. By leaving unused request methods open, it increases the attack surface and exposes the site to potential attacks. Bad actors attempt to exploit vulnerabilities in the server configuration by sending crafted requests or malformed request methods.

<LimitExcept POST GET HEAD OPTIONS>
  Order allow,deny
  Require all denied
</LimitExcept>

Block IP Addresses and Ranges

Known IP addresses of spammers or other bad actors are blocked from accessing the site in the following directive. If you monitor the server log files for any period of time, you will find certain IP addresses are constantly scanning for vulnerabilities. Specific addresses or ranges can be blocked by adjusting the RewriteCond lines. Replace the addresses in the example with the appropriate address numbers or ranges. If a user with a listed IP address attempts to connect to the site, the request fails and no further directives are evaluated. This directive can quickly grow too large to realistically maintain. Consider leveraging stronger firewall solutions for easier and automated blocking.

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{REMOTE_ADDR} ^10\.0\.0\.1 [OR]
  RewriteCond %{REMOTE_ADDR} ^10\.32\.64\.250
  RewriteRule ^ - [NC,F,L]
</IfModule>

Block Referrer Links

If the site receives referral traffic from either undesirable sites or entire generic top-level domains, this directive prevents incoming traffic from those sites when the supplied referrer information matches the RewriteCond lines. While this does not prevent other sites from simply including a link on their site or copying content, it does block incoming traffic from those sites. In the example, traffic from all .cc, .eu. and .ru top-level domains are blocked as well as traffic from example1.com and example2.com. When the referrer data matches either condition, the request fails and no further directives are evaluated. Adjust the conditions to meet the specific needs of your site as this directive is rather restrictive.

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{HTTP_REFERER} ^http(?:s)?://(?:.*)?\.(?:cc|eu|ru)(?:/.*)?$ [NC,OR]
  RewriteCond %{HTTP_REFERER} ^http(?:s)?://(?:.*\.)?(?:example1.com|example2.com)(?:/.*)?$ [NC]
  RewriteRule ^ - [NC,F,L]
</IfModule>

Block HTTP/1.0 Requests

The following directive blocks any user attempting to access the site using HTTP/1.0. I chose to block this protocol because it is outdated and I assume any connection attempt using this protocol is likely not coming from a typical user. If a user attempts to connect to the site using HTTP/1.0, the request fails and no further directives are evaluated.

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{SERVER_PROTOCOL} ^HTTP/1\.0$
  RewriteRule ^ - [NC,F,L]
</IfModule>

Block Requests with Empty HTTP_USER_AGENT String

This directive assumes that all typical users have an HTTP_USER_AGENT string which signals to the server the product name / browser and version used to access the site. If the user-agent string is blank or whitespace or dashes, then the request fails and no further directives are evaluated.

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{HTTP_USER_AGENT} ^(?:\s|-)*$
  RewriteRule ^ - [NC,F,L]
</IfModule>

Block Access to WordPress Configuration Files and XML-RPC

If a user attempts to access a file listed in the directive, the request fails. The files listed here are common configuration files or log files for WordPress which should not be exposed to general users for security reasons. The XML-RPC endpoint is commonly used by bad actors for DDoS and brute force attacks since it provides programmatic remote access to a site.

<FilesMatch "wp-config\.php|xmlrpc\.php|error_log|readme\.html|license\.txt|wp-config-sample\.php">
  Order allow,deny
  Require all denied
</FilesMatch>

Restrict WordPress Login to Known IP Addresses

This directive restricts access to wp-login to specific IP addresses or ranges. If an installation only has a handful of registered users, this directive limits login attempts to those known user IP addresses. This reduces a bad actor’s ability to brute force login attempts. Ensure the allowed IP list includes the site’s public IP address otherwise WordPress will indicate an issue in Site Health because it is unable to perform loopback requests. Replace the addresses in the example with the appropriate address numbers or ranges. Please keep in mind that this is very restrictive and you will not be able to login if you are accessing the site from a different IP address, e.g. mobile.

<FilesMatch "wp-login\.php">
  Order deny,allow
  deny from all
  allow from 172.16.0.1
  allow from 172.30.254.1
</FilesMatch>

Block Hotlinking

The following directive prevents hotlinking of images and common files. Hotlinking occurs when another site links directly to an image/file on your site instead of hosting the image/file itself. If the other site has high traffic, it will consume your site’s bandwidth and potentially degrade server performance. It may also have a cost impact if your bandwidth threshold is exceeded. Adjust the RewriteCond to your domain(s). Again, if the request is not coming from a specified domain, then the request fails and no further directives are evaluated.

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{HTTP_REFERER} !^$
  RewriteCond %{HTTP_REFERER} !^http(?:s)?://(?:.*\.)?dalesandro\.net(?:/.*)?$ [NC]
  RewriteRule \.(?:jpe?g|gif|png|svg|webp|zip|rar|pdf)$ - [NC,F,L]
</IfModule>

Block Direct Access to /wp-includes

This section restricts direct access to the wp-includes directory which contains core WordPress code and scripts. Public access to this directory is not required or intended for a WordPress site to function so direct access should be restricted.

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^wp-admin/includes/ - [NC,F,L]
  RewriteRule !^wp-includes/ - [S=3]
  RewriteRule ^wp-includes/[^/]+\.php$ - [NC,F,L]
  RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [NC,F,L]
  RewriteRule ^wp-includes/theme-compat/ - [NC,F,L]
</IfModule>

Block WordPress Author Scans

Author scans are performed to enumerate usernames for WordPress based sites. Any usernames identified through these scans can be used in brute force attempts to access the admin section of the site. If you browse to a WordPress site and include /author=1 (or 2, 3, 4, etc.), WordPress will return author details for that particular ID. This directive blocks these requests.

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteCond %{QUERY_STRING} (author=\d+) [NC]
  RewriteRule ^ - [NC,F,L]
</IfModule>

Return 410 Gone Response for Deleted Posts/Pages

While not security focused, this is an example of how to handle removed posts/pages. The “G” flag in the RewriteRule returns a 410 Gone response status code which informs browsers and bots that the resource has been intentionally removed.


<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{REQUEST_URI} ^/post-permalink(?:/.*)?$ [OR,NC]
  RewriteCond %{REQUEST_URI} ^/2021/02/28/another-post-permalink(?:/.*)?$ [OR,NC]
  RewriteCond %{REQUEST_URI} ^/yet-another-post-permalink(?:/.*)?$ [NC]
  RewriteRule ^ - [NC,G,L]
</IfModule>

Permanent Redirects (301) for Renamed Permalinks

Again, not a security focused example, but this example demonstrates how to handle changed permalinks. If the permalink for a specific post/page has changed, these directives return a 301 Moved Permanently response status code. This provides browsers/bots with the new resource location for the redirect and avoids a 404 Not Found response status code while informing bots to update its index. Adjust the RewriteCond to your domain(s).

RedirectMatch 301 ^/post-permalink(?:/.*)?$ https://www.dalesandro.net/updated-post-permalink/
RedirectMatch 301 ^/another-post-permalink(?:/.*)?$ https://www.dalesandro.net/replacement-permalink/

RedirectMatch may also be used for more significant and sitewide changes to the permalink structure such as changing from the “Day and name” permalink structure which includes the year, month, and day followed by the post name, e.g. /2013/07/06/sample-post/, to the “Post name” permalink structure which only includes the post name, e.g. /sample-post/. Assuming the site has been previously indexed under the old permalink structure, the search results will return the old “Day and name” URLs which will generate 404 Not Found response status codes.

This directive returns the more appropriate response code of 301 (moved permanently) and redirects users to the appropriate URL in the new permalink structure (assuming a change from “Day and name” to “Post name”).

RedirectMatch 301 ^/([0-9]{4})/([0-9]{2})/([0-9]{2})/(?:.*)$ https://www.dalesandro.net/$4

Reduce Automated WordPress Comment Spam

WordPress sites that allow comment submissions will receive comment spam. These comments are usually submitted through automated bots. In many cases, these bots don’t actually submit the comment through the form displayed on a post/page. Instead, the comment is submitted programmatically and directly through wp-comments-post.php. This directive assumes that any POST submissions to wp-comments-post.php are spam where the referrer is not your own domain or the user-agent string is empty. When these conditions are met, then the request fails and no further directives are evaluated. Adjust the RewriteCond to your domain(s).

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{REQUEST_METHOD} POST [NC]
  RewriteCond %{REQUEST_URI} wp-comments-post\.php [NC]
  RewriteCond %{HTTP_REFERER} !^http(s)?://(.*\.)?dalesandro\.net(/.*)?$ [OR,NC]
  RewriteCond %{HTTP_USER_AGENT} ^(?:\s|-)*$
  RewriteRule ^ - [NC,F,L]
</IfModule>

Add Secure Headers

The following section adds secure headers to HTTP responses. In modern browsers, these headers help prevent some vulnerabilities such a cross-site scripting and clickjacking. These should be adjusted for your particular site. For more information, review OWASP Secure Headers Project.

<IfModule mod_headers.c>
  Header set X-XSS-Protection "1; mode=block"
  Header set X-Frame-Options "DENY"
  Header set X-Content-Type-Options nosniff
  Header set Referrer-Policy "same-origin"
</IfModule>

Add HTTP Compression

With the exception of already compressed file types, e.g. image files, zip/rar, pdf, the following directive instructs Apache to compress any other served files. While this isn’t security related, it reduces bandwidth consumption and improves performance.

<IfModule mod_deflate.c>
  SetOutputFilter DEFLATE
  <IfModule mod_setenvif.c>
    SetEnvIfNoCase Request_URI \.(?:jpe?g|gif|png|svg|webp|zip|rar|pdf)$ no-gzip dont-vary
  </IfModule>
</IfModule>

Add Browser Caching Directives

In addition to HTTP compression above, the following directive instructs browser how long to cache certain file types. Obviously, this will reduce bandwidth consumption and improve performance since browsers won’t request the same file over and over with each post/page view. These file types are assumed to be mostly static and remain unchanged for long periods of time. The cache time should be adjusted to meet your own requirements and file rules should be added/removed as needed.

<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresDefault "access plus 1 month"
  ExpiresByType text/cache-manifest "access plus 0 seconds"
  ExpiresByType text/html "access plus 0 seconds"
  ExpiresByType text/xml "access plus 0 seconds"
  ExpiresByType application/xml "access plus 0 seconds"
  ExpiresByType application/json "access plus 0 seconds"
  ExpiresByType application/rss+xml "access plus 1 hour"
  ExpiresByType application/atom+xml "access plus 1 hour"
  ExpiresByType image/x-icon "access plus 1 year"
  ExpiresByType image/gif "access plus 1 year"
  ExpiresByType image/png "access plus 1 year"
  ExpiresByType image/jpg "access plus 1 year"
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType image/webp "access plus 1 year"
  ExpiresByType image/svg+xml "access plus 1 year"
  ExpiresByType image/vnd.microsoft.icon "access plus 1 year"
  ExpiresByType video/ogg "access plus 1 year"
  ExpiresByType audio/ogg "access plus 1 year"
  ExpiresByType video/mp4 "access plus 1 year"
  ExpiresByType video/webm "access plus 1 year"
  ExpiresByType video/mpeg "access plus 1 year"
  ExpiresByType text/x-component "access plus 1 year"
  ExpiresByType font/ttf "access plus 1 year"
  ExpiresByType font/otf "access plus 1 year"
  ExpiresByType font/woff "access plus 1 year"
  ExpiresByType font/woff2 "access plus 1 year"
  ExpiresByType application/font-woff "access plus 1 year"
  ExpiresByType application/x-font-ttf "access plus 1 year"
  ExpiresByType font/opentype "access plus 1 year"
  ExpiresByType application/vnd.ms-fontobject "access plus 1 year"
  ExpiresByType text/css "access plus 1 month"
  ExpiresByType text/javascript "access plus 1 month"
  ExpiresByType text/x-javascript "access plust 1 month"
  ExpiresByType application/javascript "access plus 1 month"
  ExpiresByType application/x-javascript "access plus 1 month"
  ExpiresByType application/pdf "access plus 1 month"
  ExpiresByType application/zip "access plus 1 month"
  <IfModule mod_headers.c>
    Header append Cache-Control "public"
  </IfModule>
</IfModule>

Everything All Together

Finally, this is the complete .htaccess file containing all of the directives reviewed above.

Options All -Indexes

ServerSignature Off

<LimitExcept POST GET HEAD OPTIONS>
  Order allow,deny
  Require all denied
</LimitExcept>

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{REMOTE_ADDR} ^10\.0\.0\.1 [OR]
  RewriteCond %{REMOTE_ADDR} ^10\.32\.64\.250
  RewriteRule ^ - [NC,F,L]
</IfModule>

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{HTTP_REFERER} ^http(?:s)?://(?:.*)?\.(?:cc|eu|ru)(?:/.*)?$ [NC,OR]
  RewriteCond %{HTTP_REFERER} ^http(?:s)?://(?:.*\.)?(?:example1.com|example2.com)(?:/.*)?$ [NC]
  RewriteRule ^ - [NC,F,L]
</IfModule>

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{SERVER_PROTOCOL} ^HTTP/1\.0$
  RewriteRule ^ - [NC,F,L]
</IfModule>

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{HTTP_USER_AGENT} ^(?:\s|-)*$
  RewriteRule ^ - [NC,F,L]
</IfModule>

<FilesMatch "wp-config\.php|xmlrpc\.php|error_log|readme\.html|license\.txt|wp-config-sample\.php">
  Order allow,deny
  Require all denied
</FilesMatch>

<FilesMatch "wp-login\.php">
  Order deny,allow
  deny from all
  allow from 172.16.0.1
  allow from 172.30.254.1
</FilesMatch>

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{HTTP_REFERER} !^$
  RewriteCond %{HTTP_REFERER} !^http(?:s)?://(?:.*\.)?dalesandro\.net(?:/.*)?$ [NC]
  RewriteRule \.(?:jpe?g|gif|png|svg|webp|zip|rar|pdf)$ - [NC,F,L]
</IfModule>

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^wp-admin/includes/ - [NC,F,L]
  RewriteRule !^wp-includes/ - [S=3]
  RewriteRule ^wp-includes/[^/]+\.php$ - [NC,F,L]
  RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [NC,F,L]
  RewriteRule ^wp-includes/theme-compat/ - [NC,F,L]
</IfModule>

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteCond %{QUERY_STRING} (author=\d+) [NC]
  RewriteRule ^ - [NC,F,L]
</IfModule>

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{REQUEST_URI} ^/post-permalink(?:/.*)?$ [OR,NC]
  RewriteCond %{REQUEST_URI} ^/2021/02/28/another-post-permalink(?:/.*)?$ [OR,NC]
  RewriteCond %{REQUEST_URI} ^/yet-another-post-permalink(?:/.*)?$ [NC]
  RewriteRule ^ - [NC,G,L]
</IfModule>

RedirectMatch 301 ^/post-permalink(?:/.*)?$ https://www.dalesandro.net/updated-post-permalink/
RedirectMatch 301 ^/another-post-permalink(?:/.*)?$ https://www.dalesandro.net/replacement-permalink/

RedirectMatch 301 ^/([0-9]{4})/([0-9]{2})/([0-9]{2})/(?:.*)$ https://www.dalesandro.net/$4

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{REQUEST_METHOD} POST [NC]
  RewriteCond %{REQUEST_URI} wp-comments-post\.php [NC]
  RewriteCond %{HTTP_REFERER} !^http(s)?://(.*\.)?dalesandro\.net(/.*)?$ [OR,NC]
  RewriteCond %{HTTP_USER_AGENT} ^(?:\s|-)*$
  RewriteRule ^ - [NC,F,L]
</IfModule>

<IfModule mod_headers.c>
  Header set X-XSS-Protection "1; mode=block"
  Header set X-Frame-Options "DENY"
  Header set X-Content-Type-Options nosniff
  Header set Referrer-Policy "same-origin"
</IfModule>

<IfModule mod_deflate.c>
  SetOutputFilter DEFLATE
  <IfModule mod_setenvif.c>
    SetEnvIfNoCase Request_URI \.(?:jpe?g|gif|png|svg|webp|zip|rar|pdf)$ no-gzip dont-vary
  </IfModule>
</IfModule>

<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresDefault "access plus 1 month"
  ExpiresByType text/cache-manifest "access plus 0 seconds"
  ExpiresByType text/html "access plus 0 seconds"
  ExpiresByType text/xml "access plus 0 seconds"
  ExpiresByType application/xml "access plus 0 seconds"
  ExpiresByType application/json "access plus 0 seconds"
  ExpiresByType application/rss+xml "access plus 1 hour"
  ExpiresByType application/atom+xml "access plus 1 hour"
  ExpiresByType image/x-icon "access plus 1 year"
  ExpiresByType image/gif "access plus 1 year"
  ExpiresByType image/png "access plus 1 year"
  ExpiresByType image/jpg "access plus 1 year"
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType image/webp "access plus 1 year"
  ExpiresByType image/svg+xml "access plus 1 year"
  ExpiresByType image/vnd.microsoft.icon "access plus 1 year"
  ExpiresByType video/ogg "access plus 1 year"
  ExpiresByType audio/ogg "access plus 1 year"
  ExpiresByType video/mp4 "access plus 1 year"
  ExpiresByType video/webm "access plus 1 year"
  ExpiresByType video/mpeg "access plus 1 year"
  ExpiresByType text/x-component "access plus 1 year"
  ExpiresByType font/ttf "access plus 1 year"
  ExpiresByType font/otf "access plus 1 year"
  ExpiresByType font/woff "access plus 1 year"
  ExpiresByType font/woff2 "access plus 1 year"
  ExpiresByType application/font-woff "access plus 1 year"
  ExpiresByType application/x-font-ttf "access plus 1 year"
  ExpiresByType font/opentype "access plus 1 year"
  ExpiresByType application/vnd.ms-fontobject "access plus 1 year"
  ExpiresByType text/css "access plus 1 month"
  ExpiresByType text/javascript "access plus 1 month"
  ExpiresByType text/x-javascript "access plust 1 month"
  ExpiresByType application/javascript "access plus 1 month"
  ExpiresByType application/x-javascript "access plus 1 month"
  ExpiresByType application/pdf "access plus 1 month"
  ExpiresByType application/zip "access plus 1 month"
  <IfModule mod_headers.c>
    Header append Cache-Control "public"
  </IfModule>
</IfModule>

# BEGIN WordPress
# The directives (lines) between "BEGIN WordPress" and "END WordPress" are
# dynamically generated, and should only be modified via WordPress filters.
# Any changes to the directives between these markers will be overwritten.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

# END WordPress

Obtaining the Certified Information Systems Security Professional (CISSP) Credential

Researching the CISSP

In February 2019, I decided to start my journey toward obtaining the CISSP (Certified Information Systems Security Professional) credential. The CISSP is a certification covering a wide range of information security topics. It is consistently included in annual top ten lists of the best IT certifications.

At that point in my career, I had 18 years of professional experience across various aspects of IT and, during the last two years of that time, I had become more directly involved in information security. After spending time reading about the exam format, the domains covered by the exam, horror stories about its difficulty, and some healthy procrastinating, I began studying at the beginning of May 2019.

As part of my initial research, I read seemingly endless posts in which people claimed to study 8+ hours per day for months to prepare for the exam. I also found a number of posters who claimed to sit for the exam with minimal (or zero) study. How much time should you spend studying? It depends! The amount of preparation needed to pass the exam depends entirely on you — your educational background, your professional work experience, and your ability to absorb the study materials. The domains covered by the exam are broad and many people describe the exam as a mile wide and an inch deep. I assumed that I wouldn’t need much preparation given my background and experience. I was wrong.

Studying for the CISSP Exam

Between family and work commitments, I limited my study time to a few hours of reading during the early hours on weekends. I set a goal to read a chapter per week from (ISC)2 CISSP Certified Information Systems Security Professional Official Study Guide. WARNING — this book is dense, dry, and repetitive. I found it to be a difficult read and I used 3 months (May through July) to finish it.

While the study guide has 20 practice questions at the end of each chapter, I didn’t attempt them until I finished reading the entire book. Thinking back, I probably should have attempted them immediately after finishing a chapter or a week later at most. The questions uncovered that I didn’t retain a lot of detail from each of the chapters over that 3 month period. I was scoring in the neighborhood of 60% in each chapter. It was an alarming realization because I thought I was retaining much more.

I knew I needed to do something more than reread the study guide so I supplemented my studies with free audio resources from Kelly Handerhan available on Cybrary. This allowed me to take advantage of my commute and add about 2 hours per day of study time in addition to any weekend reading. From August through mid-October, I probably listened to Kelly’s course 3 times completely and some individual lessons many more times.

PARENTING HACK — The audio course served a dual purpose as it is apparently boring to children (and most adults) and can put kids to sleep if played in the car for everyone to hear.

From September until a few days prior to the test, I used the Boson test simulator almost daily for practice questions. This software helped me identify weak points and also exposed me to material beyond the study guide. The software provides very detailed explanations of the answers and why one answer was more correct than the other choices. The software allows you to configure custom exams to focus on troublesome domains or on questions that are consistently answered incorrectly. The software is worth the additional cost and you can occasionally find discount codes on the CISSP Reddit group.

Up to this point, I had not scheduled the exam. I didn’t want to lock myself into a date but by mid-September I felt ready. I had been preparing for over 4 months and I didn’t want to prolong it. I scheduled the exam for mid-October. Also during this period, I significantly increased my time spent reading. I revisited the Official Study Guide and started the Eleventh Hour CISSP: Study Guide. I also branched out to YouTube videos to help me better understand concepts where I had lower practice scores. I know I went too deep into the technical aspects of networking, but at least the videos were interesting and I learned (or refreshed my memory) on many topics I had not touched in a while.

Exam Day

I was very nervous on exam day. I felt like I wasn’t ready but I had to go for it. I was tired of studying, taking practice questions, and generally worrying about the exam. All I will say about the exam itself is that it is challenging. When the first question popped up I had a moment of panic, but I took my time and tried to calm my nerves. In the end, I finished the exam in roughly 90 minutes at question 100.

After the Exam

Once I passed the exam, I received an email with instructions to complete the application for the CISSP credential. I completed the application and requested an endorsement from another active CISSP. This took about 5 days for me to complete after passing the exam. From this point, it takes another 6 to 8 weeks for (ISC)2 to review and approve the application. I received an email that my application was approved exactly 4 weeks post submission and membership fees were due. After paying the fees, I received a confirmation that I was officially a CISSP! The welcome package with printed certificate, membership card, and CISSP pin arrived about a month later.

Final Notes

To answer the question of how long does it take to prepare for the CISSP — for me it took 5 and a half months from May through mid-October with varying amounts of weekly study time.

Months 0-35 hours per week reading
Month 4• 5 hours per week reading
• 10 hours per week listening to Cybrary audio
• 1 hour per week on practice questions
Months 5-5.5• 20 hours per week reading
• 10 hours per week listening to Cybrary audio
• 5 hour per week on practice questions

One other point to note is the cost to obtain the CISSP (all amounts here are in USD and as of 2019). It’s not inexpensive. The exam itself is $699 and the annual membership fees are $125. I also purchased two books totaling $55 and the Boson software for $85 (after discount). My total cost was $964. Keep in mind that the exam fee is required each time you take the exam. I was very hesitant to schedule the exam too early because I didn’t want to risk not passing and paying another $699.

Resources

(ISC)2 CISSP

CISSP – Certified Information System Security Professional Subreddit

CISSP 16-week Study Guide, Resources, and Links to Source Documents

(ISC)2 CISSP Certified Information Systems Security Professional Official Study Guide

Eleventh Hour CISSP: Study Guide (Syngress Eleventh Hour)

Cybrary – CISSP Training with Kelly Handerhan

Boson ExSim-Max for CISSP

Kelly Handerhan – Why you will pass the CISSP

Larry Greenblatt – Pass the 2018 CISSP with Kirk & Spock

Stacks on Stacks…of Floppy Disks

There is an episode of White Collar called Uncontrolled Variables where a company uses 8-inch floppy disks to store and secure sensitive information. The premise is that the 8-inch storage medium and file formats are so old and obsolete that no one would be able to access the contents of the disk. While it makes for an entertaining episode, I wouldn’t use this method to secure my information.

Meanwhile, back in real life, I found stacks of 3½-inch floppy disks sitting in a box untouched for 20 years. The labels had been crossed off and rewritten multiple times over the years. Do I really want what’s on a disk labeled “MS-DOS 6.0 Backup 7 of 16”? I couldn’t trust the labels and I was concerned that the contents may include information that remains sensitive over long periods of time such as personally identifiable information.

No big deal, right? I’ll pop the disks into my computer’s disk drive and start reviewing. Oh — I didn’t put a floppy drive in my machine when I built it. I’ll try the laptop. No floppy drive there either. Hmm… Maybe I’ll use the disks to play dominoes. (If you’re wondering, I tried and I couldn’t get them to stand upright on their own.) Luckily, 3½-inch floppy disk readers are still readily available online and at a reasonable cost. I ordered one of these drives and, when it arrived, I went to work attempting to read the disks.

3½-inch Floppy Disks
3½-inch Floppy Disks

While the initial problem was solved, a new problem emerged. I realized immediately that most files on the disks were 20 to 25 years old (obviously since the disks hadn’t been touched in that long). The second observation was that a surprisingly large number of files could be stored on a single disk with a mere 1.44 megabyte capacity. Through another stroke of luck, most of the files were in a version of the WordPerfect file format readable in Microsoft Word. With other files, I had to look at the binary and do a little research to identify the format. In many cases, these files were also saved without file extensions or the extensions were nonsense. In the end, I was able to find utilities online to read and convert to more current formats. I was also amazed that most of the disks were still readable. Only a few disks had issues where I couldn’t access all of the files.

Given this experience, I certainly wouldn’t use 3½-inch disks as an information security solution proposed in White Collar. It’s still too easily accessed to provide the level of obstacle. Maybe 8-inch disks are better, but I’ll stick with physically secured offline encrypted drives.