Email Subscription Form

Saturday, August 31, 2019

Break Your App With This One Weird Trick

I missed a bug recently, more than once, and I'm kicking myself about it.  This post is about that bug and how you should make sure to always run a test for it. It's also about how to keep from repeating your mistakes.

Here's what happened: as I mentioned in a previous post, I'm currently testing file uploads and downloads.  When the developers on my team first coded the upload functionality, I dutifully tested all kinds of file names: long names, short names, names without extensions, names with capital letters, etc.  Everything looked great, but I missed one important test: testing file names with spaces.  Every file name I had tested with was one single word, like "sunrise.jpg".  I forgot to test with a name like "Grand Canyon.jpg", and as it turned out, uploads with spaces in them weren't working correctly.

Spaces are SO easy to forget to test, because they are literally invisible!  Testing with spaces applies to any text fields, not just file names.  For example, when you are testing a first name field, make sure to test with two first names, like "Mary Jo".

It's also important to remember to test with a space at the beginning of the text and at the end of the text.  Your developer should be trimming these whitespaces when processing input, but if he or she forgets, this can mean trouble.  You may wind up with a situation where you have a list of names sorted by last name, and the name "Smith" is appearing at the top.  This is probably because someone entered " Smith" with a leading space.

Similarly, your users might have problems logging in to your application, even though they are sure they have the right username.  This may be because when they set their username, they accidentally put a space at the end of the username, and that space was not trimmed by the developer.  So they are trying to log in with "catLover" when they should really be logging in with "catLover ".

Back to my story: after the bug with file uploads was discovered and fixed, I carefully retested uploads again, this time being sure to include file names with spaces in the beginning, middle, and end of the file.

Our next development task was to be able to resize a file upon request.  When this functionality was ready, I started running all kinds of tests: resizing by height only, width only, height and width, resizing various file types, etc.  While I was testing, the developer who worked on the feature mentioned that he had just discovered a bug: the files wouldn't resize if they had spaces in the names, because the spaces weren't being encoded properly.  I'd like to think I would have discovered this eventually, but who knows?  I was more focused on the new functionality than I was on regression testing.

The bug was fixed, and again I carefully tested it.  I added spaces in the beginning, middle, and end of the file name, and I used every kind of special character on my keyboard.  Surely, I thought, this must be the end of this bug.

Our next development task was to do file size and type checking.  We didn't want to upload a file if it was larger than the application was told to expect, or if it had the wrong file type.  I tested with files with all kinds of sizes and types, and all kinds of mismatches.  With each mismatch, I verified that the file was checked and rejected.  I did a great deal of regression testing as well.  It occurred to me to test with different file names, but since this I had already verified that file names of all kinds were working with resizing, it seemed like overkill, so I chose not to do it.

I was wrong!  As it turned out, the system that was doing the file checking was different from the system that was doing the file resizing, and once again, spaces in the file name weren't being encoded properly.  A developer noticed that every file that had spaces in the name wasn't being checked by the system.  I was bitten by the file space bug once again.

I hate making mistakes, and I hate missing bugs!  Fortunately these bugs were caught before they impacted any end users.  But I like to learn from my mistakes, and I've taken the following steps to make sure I don't miss this bug ever again:

  • I've put a sticky note that says "Spaces" on my desk.  Whenever I see it, I will be reminded to test for spaces in my inputs.  I did this years ago when I kept forgetting to test on different browsers.  I had a "Test on IE" note on my monitor, and it helped me develop the habit.
  • I've added file names with spaces to my saved Postman collections.  This way, even if I forget to test with spaces, the saved requests will remember for me.
  • I've written this blog post to serve as a cautionary tale to myself and others.

One final lesson I've learned from this is that different parts of the same feature can process information differently.  The feature is using new technologies that I've never tested before, and I assumed file names would be handled in the same way for uploads, resizes, and type checking. But that was not the case.  When you are dealing with new technologies, be sure to regression test everything, even things you think might not need to be covered.

It's amazing how much one simple space can break!  When you are testing anything that can accept text, from a simple form field to a file upload, be sure to remember those invisible spaces.

Saturday, August 24, 2019

Let's Go Deep! Part III: Internet Routing

In the last two posts, we've been going deep, learning about how information is transmitted over the Internet.  In Part I, we learned how data is divided into packets, sent to its destination and reassembled.  In Part II, we learned how data sent over the Internet can be encrypted and protected.  But how does data know how to get from one IP address to another?  This is the subject of today's post.

Every device that can be connected to the Internet has a network interface card (NIC), which is a piece of hardware installed on the device that allows it to make a network connection.  Each NIC has a media access control (MAC) address that is specific to only that device.

A modem is a device that connects to the Internet.  Other devices can connect to the modem in order to receive Internet transmissions.  A wireless router is capable of receiving wifi transmissions.  The router connects to the modem, and other devices connect wirelessly to the router in order to receive data from the Internet.  Many Internet service providers (ISPs) provide customers with a combination modem/router, which connects to the Internet and sends and receives wireless signals.

In Part I, we learned that every device connected to the Internet has an IP address.  An IP address is different from a MAC address in that the MAC address is assigned by the manufacturer of the device, and an IP address is assigned by the network.  An IP address has two sections, called the subnet and the host.  The subnet refers to one subsection of the entire Internet.  The host is the unique identifier for the device on the network.  The IP address combines these two numeric values using bitwise operations.  You can't look at an IP address and say that the numbers on the left make up the subnet and the numbers on the right make up the host; it doesn't work that way.  The IP address is more like a sum of two long numbers.

As mentioned in Part I, when a packet of data is sent from a server to a client, it is sent with the IP address of the destination.  In order to get to that destination, the packet will hop from one network to another.  The routing protocol of the Internet is called the Border Gateway Protocol (BGP).  This is a system that helps determine a route that will traverse the least number of networks to get to the destination.  Every router in a network has a series of routing tables, which are sets of directions for how to get from one network to another.

When a packet of information is first sent to the network's router, it looks at the IP address of the destination and determines if the directions to the destination are available in the routing tables.  If they are not, the BGP is used to determine the next logical network where the packet should be sent.  A gateway is an entrance to a network, and a default gateway is the address that the request is sent to if there's no knowledge of a specific address in that network.  When a packet arrives at the new gateway, the BGP calculates the next appropriate destination.

After traversing through networks in this way, eventually the packet arrives at the router for the network that contains the IP address of the destination.  The router determines the MAC address of the destination and sends the packet to that address.

One more important feature of networking is the use of a proxy server.  A proxy server is a server that is positioned between the client and the destination server.  It is configured so that any requests your client makes will go through the server before it gets to its destination.  There are many uses for a proxy server; the main use is to keep the actual address of a site or a router private.  Proxy servers can also be used by hackers to intercept requests, especially on a public network.  Finally, proxy servers are a great way to do security testing!  Using a tool like Fiddler or Burp Suite, you can intercept the requests that you make to your application and the responses you receive in return.  You can learn more about how to use Burp Suite in this post.

This concludes my three-part series on how the Internet works.  I hope that you have found it helpful, and that you can apply these concepts when testing!

Saturday, August 17, 2019

Let's Go Deep! Part II: Encryption, Tokens, and Cookies

In last week's post, we talked about how HTTP works to pass information from a server to a browser.  But when information is passed back and forth between systems, we need to make sure that it's protected from being intercepted by others for whom it was not intended.  That's why HTTPS was created.  In this week's post, we'll talk about how encryption is used in HTTPS, what the difference is between cookies and tokens, the different types of cookies, and how cookies can be protected.

How HTTPS Works:

When two systems communicate with each other, we refer to them as the client and the server.  The client is the system making the request, such as a browser, an application, or a mobile device, and the server is the system that supplies the information, such as a datastore.  HTTPS is a method of securely transmitting information between the client and the server.  HTTPS uses SSL and TLS to encrypt the data being transmitted and decrypt it only when it arrives at its destination.  SSL (Secure Sockets Layer) and TLS (Transport Layer Security) are both tools for the encryption and decryption of data; TLS is a newer version of SSL.  

Here's how TLS works: before any data is transmitted, the client and the server first perform a handshake.  The handshake begins with the client contacting the server with a suggested encryption method and the server responding back agreeing to use that encryption method.  It then continues with the client and the server swapping certificates.  A certificate is like an ID card; it verifies the identity of the client or server.  The certificates are checked against the CA (Certificate Authority), which is a third-party entity that creates certificates specifically for the purpose of HTTPS communication.  

Once the certificates are swapped and verified, the client and the server swap decryption keys, and the handshake is completed.  Now the data is encrypted, transmitted from the server to the client, and decrypted once it arrives at the client safely.  

Session Cookies and Tokens:

Another important way data is secured is through the use of session cookies or tokens.  Session cookies and tokens are strings that are passed in with a client's request to verify that the person making the request has the right to see the data requested.  The main difference between session cookies and tokens is that a session cookie is stored both on the client and the server, and a token is only stored on the client.  

In systems that use tokens, the token is created when a user logs in.  The token is made up of encrypted information that identifies the user.  The token is stored in local storage in the client's browser and is sent with every HTTPS request to the server.  When the server receives the token, it decrypts it, validates the user's information, and then returns the response.  

The most popular system of tokens in use today is JWT (JSON Web Token).  You can read more about JWTs in this helpful documentation.  

A session cookie is a unique string that is created by the server when the user logs in.  It is saved in the server's datastore as a session id.  The server returns the cookie to the client, and the client saves it in the browser while the session is active.  Whenever the client makes a request to the server, it sends the cookie with the request.  The server then compares the cookie with the one it has saved to make sure it matches before returning the response.  

Tokens and session cookies are usually set to expire after a period of time or after an event.  For example, a token issued might be good for one hour.  Just before the hour is up, a request can be made for a new token (called a refresh token) in order to keep the user signed in.  Session cookies usually expire when the user logs out or when the browser is closed.

Persistent Cookies:

Another type of cookie used is the persistent cookie, which is a bit of data saved on the server about the user's preferences.  For example, if a user goes to a website and chooses German as the language they would like on the site, a persistent cookie will remember that information.  The next time the user goes to the site, the cookie will be examined and the site will load in German.  

Securing Cookies:

Because they are stored on the server, cookies are more vulnerable to being intercepted and used by someone other than the user than tokens are.  To help protect cookies, these flags (attributes) can be added to them at the time of creation:

  • Secure flag: ensures that the cookie can only be transmitted over HTTPS requests and not over HTTP requests.
  • HttpOnly flag: keeps a cookie from being accessed via JavaScript, which helps protect it from Cross-Site Scripting (XSS) attacks.  
  • SameSite flag: ensures that the cookie can only be sent from the original client that requested the cookie, which helps protect it from Cross-Site Request Forgery attacks.  

Hopefully this post has helped you learn how HTTPS works, and how tokens and cookies ensure a client's validity.  Researching this information certainly helped me!  But I want to go deeper: how does a message know how to get from one IP address to another?  How are HTTP requests intercepted?  And how does a router work?  I'll have answers to these questions in next week's post!  

Saturday, August 10, 2019

Let's Go Deep! Part I: How HTTP Requests Work

Recently, an astute reader of my blog pointed me to a great post about the importance of having technical skills as a software tester.  The author makes an excellent analogy: a software tester who doesn't understand technical concepts is like a surgeon who doesn't understand anatomy.  If we are going to test our applications thoroughly, we should understand the underlying systems that make them work.

I freely admit that I am not an expert in networking, or even in how the Internet works.  But I'm willing to learn, and pass that information on to you!  So let's go deep!  We'll begin this week with how HTTP requests work.

When you type an website's address into a Web browser, you are typing a URL.  A URL (Uniform Resource Locator) is simply a fancy name for a web address.  The URL contains a domain name.  The domain name identifies a specific grouping of servers on the Internet.  Examples of domain names would be or

Once the browser has the domain name, it uses it to look up the associated IP address in the DNS (Domain Name System), which is a database that contains all the mappings of domain names and IP addresses.  An IP address (Internet Protocol address) is a unique series of numbers that is assigned to every device that is connected to the Internet.  

Once the IP address is known, a connection is opened to that address using HTTP.  HTTP (HyperText Transfer Protocol) is an application protocol that allows information to be transmitted electronically.  

Once the connection is opened to the server at the IP address, a request can be made to get information from that server.  Information sent over the Internet is called a message.  The request uses TCP (Transmission Control Protocol), which is a system of delivering messages over the Internet.  

The TCP divides a message into a series of packets, which are fragments of between 1000 and 3000 characters.  Each packet has a series of headers, which include the address of the packet's destination, information about the ordering of the packets, and other important information.  

If for any reason a packet doesn't make it to its destination, the client (the address making the request) can request a packet to be resent.  Once all the packets have arrived, the client reassembles them according to the instructions in the header.

It's no secret that information sent over the Internet can be vulnerable to security attacks.  A malicious user can intercept HTTP requests and get sensitive information from them or manipulate them to make requests that will return sensitive information.  For this reason, data sent over the Internet is often encrypted or protected.  We'll learn more about this in my next post, which will be on encryption, tokens, and cookies!

Saturday, August 3, 2019

Six Tips and Four Tools for File Upload Testing

I've been testing file uploads lately, which is always fun.  It's also important, because uploading a malicious file is one of the ways that a bad actor can exploit your application, either by taking down your application, or by extracting sensitive data from it.  In this week's post, I'll offer six tips and four tools to help you be successful with testing file uploads.

Tip One: Upload Files With Allowed and Forbidden Extensions

The first step in testing file uploads is to find out what kinds of files will be allowed to be uploaded.  These files should be in the form of a whitelist, NOT a blacklist.  A whitelist specifies that only certain extensions will be allowed, whereas a blacklist specifies what is not allowed.  You can imagine that when a blacklist is used, there are dozens and dozens of file types that will be allowed, some of which you will not want in your application!  Therefore, it's important to use a whitelist instead, which will be limited to the very few types of files that you want interacting with your application.  If your developers are not using a whitelist, please share this information with them.

Once you know what the whitelisted file types are, try uploading each type.  Then try uploading a wide variety of files that are not whitelisted.  Each of those files should be rejected with an appropriate error message for the user.

Tip Two: Upload Files With Inaccurate Extensions

One of the tricks malicious users employ to upload forbidden files is to rename a malicious file with an allowed extension.  For example, a bad actor could take a .js file and rename it as a .jpg file.  If .jpg files are allowed in your application, the file might be uploaded and then executed when opened by an unsuspecting user.  So it's important for your application to have checks in place to not only verify the extension, but also to scan the file to verify its type.  

It's easy to test this by simply taking a forbidden file, renaming it to have an allowed extension, and attempting to upload the file.  The file should be rejected with an appropriate error message.  The attempt should also be logged by the application, so if there is ever an upload attempt of this kind in production your security team can be alerted.

Tip Three: Test for Maximum File Size

Your application should specify a maximum file size.  Files that are too big can cause damage to your application either by slowing it down or causing it to crash, and can even cause data to be accidentally exposed, such as in a buffer overflow exploit.  

Find out what your application's maximum file size is, and verify that files equal to and less than that size are uploaded appropriately.  Then verify that files over that maximum size are rejected with an appropriate error message.  Be sure to test with files just over the maximum size, and with files well over the maximum size.  

Tip Four: Test With Animated GIFs

Often when image uploads are allowed in an application, the .gif extension is one of the allowed types.  GIFs can sometimes contain animation.  Verify with your team whether your application will allow animated GIFs, and if not, verify what should happen if a user uploads one.  Will the file just display as a static image, or will the file be rejected?  Make sure that uploading an animated GIF does not result in a broken image on the page.  If animated GIFs are accepted, verify that it loads and displays the animation properly (see the next tip).  

Tip Five: Verify That the File Was Uploaded Correctly

It's not enough to verify that you don't get an error message when you upload a whitelisted file.  You also need to verify that the file was saved to the database correctly.  The easiest way to do this is to download the file and make sure it looks the same way it did when you uploaded it.  If your file should be displayed in the UI, you should make sure that the file looks correct in a browser or on a mobile device.  If an image that you uploaded should be resized on the page, make sure that it has resized correctly.  You don't want to have other data obscured because someone uploaded an image that's too large!  If you are expecting a video or audio file to play, make sure it's playable. 

Tip Six:  Have a Folder With File Examples for Testing

My favorite tip is to have a folder filled with files of all kinds for use in testing.  I have a folder with tons of files with different extensions and a wide variety of sizes.  This way whenever I need to test file uploads, I'm ready to go with test files and I don't have to waste time combing the internet for good examples to use.  

This brings me to my Four Tools for File Upload Testing!

I recently discovered this site when I realized that I needed some files with a .doc rather than a .docx extension.  This site definitely delivered, and it has many other example files as well.

I mentioned this tool in my Fifteen Free Tools to Help With Testing post.  When you need to test file size limitations, you can use this tool to create files of all different sizes.

This is an extensive, easy-to-read list of all the MIME types and their extensions.  It's very helpful when you want to identify less common file types to test with, or when you are wondering what MIME type goes with a certain extension.  

Tool Four: Eicar Test File

If your application has virus-checking for uploaded files, you will want to use this test file.  It is a file that is designed to look like it has a virus, but it is actually virus-free.  You may find, however, that if your computer has virus-checking you won't actually be able to download the file!  I was able to get around this by having someone send me the text of the file through chat, and then I pasted the text into the raw input window of Postman for my upload request.  

File uploads are one of my favorite things to test.  If you follow these tips and use these tools, it may become one of your favorites as well!

New Blog Location!

I've moved!  I've really enjoyed using Blogger for my blog, but it didn't integrate with my website in the way I wanted.  So I&#...