From the course: Secure Coding in Python

Dynamic typing with Python - Python Tutorial

From the course: Secure Coding in Python

Dynamic typing with Python

Python's dynamic type system can help us be extremely productive by reducing the amount of boilerplate we need to write as developers. This, however, does not come without a cost, and there are a few things we should keep in mind when working with a dynamically-typed language. Some of these things relate to coercion, specifically to truthy and falsy values. Truthy values are values that are interpreted as true in an implicit way, and falsy values are values that are interpreted as false implicitly. Let's have a look at a few examples. Truthy values include non-empty strings, populated lists, and nonzero numbers like the number 1. Falsy values include empty lists, empty strings, and the number 0. Now, let's have a look at scenarios where this can affect our applications security. So here I am in my exercise files. And from this point on, we're only going to use one pipenv environment. So I'm going to go ahead and ls, and I'll make sure that I'm in the base of the course exercise files right here. Clear my terminal. And I'll type in pipenv install. And since we have a pip file here, I should be able to set up an environment. And I'll go ahead and navigate to 02_01 example_typing. And let's quickly have a look at this code here. We have our user class, which is instantiated as either trusted or not trusted, which would be false. And by default, it's not trusted which is great. And then there's the can login method, and if the user can log in, we return that the user is trusted. Finally, we have this login utility function that basically gives all the secrets to users that are trusted and doesn't give secrets to users who are not trusted. Let's have a look at whether this works by instantiating a hacker, who is a user, or trusted is false, and a friend who is trusted, and we log in both. Now, let's go ahead and run this. So I'm going to cd into 02_01_begin, clear, and I'll type in pipenv run python example_typing.py. And oh, we have a problem. Both our users were given all our secrets. And what happened here? Well, if you look here, we assess whether the user can log in, where we would want to invoke the can_login as a function. A function happens to be truthy when it's not run. So in order to get the return, value we would have to kind of do this in our login method. So if you're using the API and you only have control over what happens here, this is what you would want to do, right? You'd want to add this. But if you're implementing this right here, the user, you want to make sure you code an API that basically is future proof can_login as a function will eventually cause somebody to make this mistake. So the first layer of defense is actually here. And what we can do is we can make this a property. And making this a property means we don't have to invoke this. So let's go ahead and run this, pipenv run python example_typing.py. Okay. So no secrets for you and all our secrets. That's great. Now, is there anything else we can do? Well, I think there are. We can also add a little bit of security here by saying if user. can_login is true, and that will help. And I'll run this again. And by the way, if I run this as a callable, does this break my code? Yes it does, which is great because breaking our code is better than having a security breach go unnoticed, especially if we have tests. So we've added is true right here, and we've created a property. Now, let's take a look at this in an application scenario, not just in code. That's almost like a toy. So let's navigate to feed. Here you'll see a post directory with a test directory and some tests in test.py. And we're checking that if a person is not authenticated, they are forbidden from getting to the posts. So I'm going to go ahead and run this. So cd feed. Clear my terminal. And if you're struggling to navigate this, I'm going to type in PWD, which helps you see that this is where you are. And clear my terminal again. And I'm going to say pipenv run pytest, and we see that our test failed. We get a status of 200, which is an okay, as opposed to a 403, which is forbidden. So what happened here? This is actually a similar scenario. Let's go ahead and navigate to Post, Views. And here we have has_permission. So request. user is authenticated is okay, but here we have this same issue we had before. So first of all, I'm going to type in if request.user.profile.is_writer is true, and then let's navigate to user profile. And that's going to be in this models.py here. And we found out that is_writer is actually not a property. And making it a property will really help us. So property. Great. Now, let's have a look, pipenv run pytest. And looks like my test has passed. Now, I could also delete this. But having this extra bit of explicit checking really goes a long way, and I really prefer this way. So having dynamic typing is great. But since we're using things like coercion and things can be very flexible, we want to make sure that we write lots of tests, and we want to make sure that we put as many checks in place to make sure we don't fall into these pitfalls with things like coercions.

Contents