AES Encryption with @nocodebackend

Learn how to build AES encryption and decryption functions in Xano. The tutorial covers generating random initialization vectors, Base64-encoding encrypted data for storage, and keeping keys in environment variables instead of the database.
Chapters
You know, a question sometimes comes up is about encryption, and like how we can best encrypt data and do so in a way that's relatively easy to work with and is yet secure. There are two main ways we can think about encryption. One is what's called symmetric encryption. That's basically we're using the same technique to both encode and decode the data. If you have a key to your house, you can be let in, and you can also lock the door. The other is called public-private key encryption, which is a somewhat more sophisticated technique where you can hand things out, hand out like a public key that will allow people to encrypt and verify messages. But you would, but only you have the key that will allow you to either decrypt or create messages that are subject to a signature. Those are cool things, but we're going to stay with a simple case for a minute because even this, what's called symmetric encryption, where you're just locking your house with a key, is one that is really, really useful., and the, the gold standard for this right now is something called AES, which is the Advanced Encryption Standard. There used to be something called DES that's still around, but it's since been shown to be more vulnerable, especially in a world of greater computational power to be able to crack those particular keys. So our job today is to figure out how we can relatively securely encrypt things using symmetric encryption, like if you want to have a message that you want to like store in a database, you'd like it to be encrypted using a key, and you don't necessarily trust the built-in encryption from your systems. So the key to AES is you're going to need two key things in order to make your encryption work. One is you're going to need to have an encryption key. This is a set of bytes or string or password or whatever, and we're going to show how we can use that. And you probably store that like in your environment, in, say, your Xano or your other backend, in order to make sure that it's not sitting in the database. That way, if somebody compromises the database, they'll get access to your environment, they're not able to decrypt anything. And the second is what's called an initialization vector. Initialization vector is a neat idea which says that when you, every time you encrypt, encrypt, you can create this additional level of randomness. We often call an IV a nonce, right? It's going to be a single non-repeated random value that's getting mixed in with the encryption process so that if someone is trying to, you know, brute force your message or whatever it is to find out what's inside or figure out what your encryption key was, they're gonna have a much harder time because of the presence of this initialization vector. There's more to it cryptographically, but essentially that's what it is, is making it harder. And that means whenever we create an encrypted payload, we are going to create two things. We're going to create both the actual encrypted text, and we're going to create this initialization vector that we use to create the encrypted text. And then we're going to deliver both the IV as well as the encrypted text to whoever needs it. And then in order for us to decrypt, we're going to need both of those things. We're going to need the original encrypted text, we're going to need that IV, and of course the encryption key that we use to lock the door. Let's talk about how this works. So we're going to add a function over here in Xano. We're going to call it encrypt_text, right? And I'm going to say this function is going to take an input, which is, I'm just going to call it source for a minute. Cool. And we're going to say in our function stack, let's, you know, we're going to create a new thing, which is going to be the set of random bytes that are going to be initialization vector. Now, How many bytes to create will depend on which encryption function we use. So, so actually let's start by saying we're going to encrypt something, um, and we're going to encrypt our source data, right? Using one of these algorithms and using one of these CBC algorithms, I think you need like a 16-bit, uh, 16-byte initialization vector. Uh, using one of these GCM algorithms, you're going to need a 12-byte initialization vector. And that means we're going to need to create the IV. In order for this to work. And we're going to need to have a key, a key we can get from the environment. But right now, I'm just going to create a very simple EFG, is the same as combination on my luggage, to just sort of set up as like a base key that we're going to work with. But the ID should be a random string of bytes, random set of bytes. In this case, it should be 12 bytes because the GCM. So we say save. And now the next thing we're going to do is we're going to create whatever the ID is going to be. Now, you might think that we should do that by doing create variable, but there's actually a good security function for this. Where it's just called random bytes. And it sort of allows you to say, well, I just need something that's 12 long. And I know that because I'm using GCM. And the return variable is going to be— I'm just going to call it initialization, initialization vector. Cool. And now I'm going to create that as the initialization vector, right? I'm going to need to drag it above because it is an input to my crypto. Right, because I'm going to feed it the initialization vector there as the IV, ABCDFG as my key, and the original source text. This will return a set of bytes that it's calling crypto1. So let's do a quick test. And we'll call this, you know, state, you know, like and subscribe to state change YouTube, right? And we'll say run. And it creates sort of this weirdo list of bytes. And this creates our first significant hurdle now post-encryption, which is those bytes are not easy for us to store. You see, the way a byte works is it's going to store numbers between 0 and 255 in each of those slots. But usually when you're looking at this text, we're not used to looking at all the possible representations that are in there. There's something called ASCII that defines how we translate those numbers between 0 and 255 into characters. Then we just look at what's called an ASCII chart here. If I just throw in a quick ASCII table into my Google, we can take a look at ASCII and probably just even use just the Wikipedia page for it just to see what the standards are in here. Right. And I can go take a look at this image. And it shows that, you know, it does in kind of a fancy way of which is all based on like how the, how the numbers work. Let's use a better table for a second. And I think we probably have one back here on Google. Yeah, let's take a look at this ASCII table here. And the idea is each of those numbers that the byte could be will be represented on your screen using some character that you've seen, right? So, because behind the scenes computers compute, they think in terms of numbers. And here we have, you know, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, right? These are the numbers that go with it. And this is the character that would be shown. You see a lot of these sort of have these square brackets, so you can't really see what they're going on. But then you get to ones that are like letters, right? So whenever you see like the letter F, and the computer is thinking the number 70. Now, this is important because what we are looking at here are the bytes. And those bytes go all the way up to 256. And the ASCII table, you know, only goes to 127. And like, so half of all of them don't really have a standard representation at all. So in order for us to store this as text and use this in places that know how to store text, we're going to need to re-encode this data. So it's not just the raw bytes, but something we can use as text. This is where Base64 becomes handy. So I'm going to say, you know, data manipulation. I'm going to, this time will be a create variable. And I'm going to call it, you know, Base64 encrypted. And the value of that's going to be the return from crypto1. I'm going to add a filter called, you know, Base64 encode. Cool. Now when I do that, if I change my response down here in crypto1 to the Base64 encrypted, You see, it looks much more friendly to us now. Now, it still looks all garbagey, right? Because you can't really make heads or tails of it. But the idea behind Base64 is that you're just going to store data 6 bits at a time instead of 100 and instead of all 256, all 8 bits at a time. That means there's 64 possible values because 2 to the 6th power is 64. And that means it's using all the lowercase letters, that's 26 of them, all the uppercase letters, that's another 26, total of 52. All the digits 0 through 9, that's another 10, that gets me to 62. And then there are a couple of other characters, as you watch this you'll find there's like, you know, plus and slash and an equal sign has a special meaning in here that we don't really need to get into. The most important thing is we use Base64 when we have byte data that we want to represent as text because it's going to be easier to manipulate and store that way. Now, the other thing we've got going on here is that if we take a look at the debugger and take a look at the random bytes that we use for initialization vector, it's got the same business going on, right? It looks like all these weird little characters. That's fine. We then create another variable, data manipulation, create variable, and I'll call it base64iv, right? And this will be based on the initialization vector and we'll apply that same base64 encode to it. Update, update. Cool. And now if I just rerun this and go back to the debugger, that last variable I created, that base64iv, just looks like this. Which is fine. You'll notice it's actually longer than 12 characters. And that's because of that difference between the fact that we're storing it 6 bits at a time and storing it 8 bits at a time, it's predictably going to be about 30% larger. Okay, now we've base64 encrypted these two things, we want to be able to store them because later in order for us to decrypt, we're going to need these two pieces of information. So the way I like to approach that kind of packaging is just through something as simple as JSON. So I'm going to create a variable, and I'm going to call it, you know, object. And I'm going to make it into the— make it into a blank object, because that's how we do things in Xano. We're going to then use the set to set— this is going to be encrypted. And this is going to be equal to— sorry, the path is just going to be encrypted. And then the value is going to be the base64 encrypted, right? So key of encrypted, a value base64. Great. And I'll do another set here. And I would say the set is going to be of, you know, IV, right, and this is going to be base64_iv. Remember, that's the initialization vector. That's the other part of the package we want to sort of send across. Using this as an object in Xano allows us to sort of keep the stuff organized. And if I just do a rerun here again, and I go to the debugger, and I go to create variable, you'll see I now have an object that has both the encrypted and the IV that we created previously. All that's fine. Now let's go to the, the next part of the process, which is we need to create a string out of this, right? So I'm gonna create a new variable and call it output, often my convention for the— if I'm I'm looking for something to come out at the end of a function, I call it output. And that output is going to be equal to the object that we just created with a couple of additional filters on it. One is we want it to know that, hey, this is an object, I'd like to represent it using JSON. JSON is something we see all the time in Xano. But the thing is, JSON is actually a string standard. This allows us to take this object, which is in theory an n-dimensional thing in memory space for the computer, and turn it into a nice string, one character after another. But Xano often runs around, goes a little crazy when things are encrypted in JSON and wants to immediately turn them back into objects. So to kind of protect that, to laminate it, if you will, I'm going to add another filter. I'm just going to say let's base64 encode that result as well. This means we get a final result that is the encrypted text, the encrypted bytes turned to base64, the IV turned into base64, combined into an object, which we then are storing as JSON. So it keeps track of those key-value pairs. And then we're rewrapping it in base64 64 all at the end. So what we wind up with is something that's going to be just this one bit of Base64 code. Now, just so we know a little bit about it, we're going to go over to CyberChef for a second, which is a favorite tool of mine, and the, um, in order to go look at that Base64 text, right? And we want to confirm that whatever's in here is still relatively encrypted. And I've already got the Base64, from Base64 recipe here. And so we can see what this thing looks like on the inside. And what does look like on the inside? Oh, you know, the reason why it looks like garbagey stuff is because the base is— it's giving a result of the base64 encrypted, not because the output. So let's fix that. Let's go back over to here. We're going to say this is the output. And we'll say save this and rerun that. Yeah, now we get something a little bit longer. Usually, you can tell base64 encoded JSON because it almost always starts with an ey. And that's a good thing to sort of keep in the back of your mind is trying to figure out whether or not something looks like it's probably the correct stuff. You'll notice that like JavaScript JSON Web Tokens, JWTs, look the same way. So if I do a paste here, hey, look at that. Now I got that down there. And if I just wanted to do a little JSON beautification, I can drag that into my recipe as well. You see, and now it shows me the encrypted, it shows me the ID, it all is relatively clean, right? These two things are now in here. This is exactly what I want to be able to store. And now I can go store that in database, I can go send that to my customer, whoever, and is now secured and encrypted in a way that will allow me to decrypt it later. So did I say decrypt it later? Let's decrypt it now. Now I have a function, right, where I can take any input, like, you know, you know, this is a great tutorial on YouTube. And I can run this and this will create any sort of payload for me, right? And that payload I know is all encrypted, it doesn't have anything that we're ever going to be able to read again, right? I can paste that into here, I can go take that encrypted text, And if I wanted to copy this and sort of paste it into here, you know, it won't work. I can even pause that and just shows me the original bytes, right? This is all encrypted bytes. This is not anything that anyone else is gonna be able to use unless they know how to decrypt it. So let's talk about how to decrypt it. We will click on the back here and go back to my list of functions. I'll create one. I'll call it decrypt text. Cool. And now I'm going to have a function which is going to be text and it's going to be encrypted payload, right? Which is going to be whatever that base64 encrypted stuff is. Now I need to reverse my steps. I'm going to go data manipulation. I'm going to go create something and I'm going to call it, you know, object, right? Why am I calling it object? I'm calling it object because I'm taking the encrypted payload, which I know is first in base64, right? So I'm taking base64 base64, and I'm going to decode that update. And now I'm going to— after I know it's base64, then I know it's JSON encoded, right? So I can take the JSON and decode the JSON as well, right? I'm just reversing those steps that before I would JSON encode it and then base64 encode it. And now I am doing it in reverse. First on the outside, the base64 decode, and then on the inside. This will give me an object that I can work with.. And I don't know whether I have here. Oh, no. Okay. So one of the things I've managed to lose in the process here, we'll see, is we're going to go over to the decrypt text here. We're just going to see if we can do a quick run and debug and see if we can paste in the code. Oh, yeah, we can look at that. That's actually the code that was the encrypted text. So this is not actually what we want to be able to decode. So let's just go quickly back here. This happens all the time. You go back and you go look for your, you know, encrypt, encrypt text. And you've got your source here. So I'm just going to go say, let's, let's encrypt some code again, right? So great tutorial on YouTube. And that's going to give me this payload here. Once again, we're looking for that ey to make sure that whatever we're working with here is actually originally JSON. So I'm set. That's great. I'm going to now go back to here. And look for my decrypt. And I'm going to do a run debug, and I'm just going to feed in this bit of information, right? Sometimes what happens when you copy and paste is you sort of do things wrong with the quotation marks. So just use the color coding in here as your, as your cue for whether or not you pasted the stuff in right. I run this and it says missing entry var1. Why? Because down here it's looking for var1. Instead of saying var1, we're going to say just show me the object. Cool. All right. Hey, look, we, we've correctly at least decoded the information. Now we need to take this information and turn it into something that is going to be more useful for us. And so to do that, we need to get the encrypted and the IV out, we need to base64 decode them, and then run them through decrypt using the key that we know. So first thing we're going to do is we're going to create a variable, I'm going to call it encrypted. And that's going to be equal to the encrypted— no, actually, the object, right? The thing that, you know, because it's the object down here, object.encrypted. But that's just gonna give me bytes. So what I want to do then is I want to do base64 decode of this as well. Update, save. And I'm gonna do the same thing here with the— no, not database request, data manipulation, create variable. And I'm going to say this is going to be my IV, which is going to be the object .id. And we'll say, and then also, I want to apply that same base64 decode on that as well, right? What I'm doing is taking the nice little string here and turning it back into those bytes that were so hard for us to look at previously. So we're gonna say save to that. And then we will— now that I've got the encrypted and the IV, the next big thing I can do is I can decrypt, because if I know those two pieces of information, and if I know my encryption key, I'm good to go. So let's talk about how we would do that. We take security, We go to decrypt. We need to know which algorithm we're using because we have to use the same algorithm for both encoding and decoding. If I encode using CBC and I decode using GCM, I will get no good answer, right? So I need to know what was the original encrypted bytes. That's what encrypted is. I need to know what's my key. And that's, remember, we did this, you know, in my Spaceballs joke before. And then the, and then I have my initialization vector. And when I put the initialization vector with the data with the key and use the correct algorithm, this should all give me the answer of my decrypted stuff. So let's find out if that works. We'll do that by having the output be the result of that decrypted string. And we'll try running this again. Look at that. So now we have the mechanisms by which we can both encode— which we can encrypt the data, we can encode it in a way that's easy for us to store in the database or store sending to somebody else or to a third party or whatever. And then finally, the ability to decode this information first by decoding it through the basic 64 into the JSON, and then finally running it through encryption so we get our correct data back at the end. And because we've turned this into two functions, uh, we don't need to put this into all the different parts of our code. This is one of the great things about Zano is this idea of reusable functionality, because we've wrapped all the concern of how we deal with how we package, uh, those initialization vectors, uh, where we get the keys from, all that into these functions that have a nice simple input and output. So if I wanted to, I could go back to functions here, here. And I could, you know, say, oh, what, I don't want to go to add-ons. Thank you, functions. And I would say add function. And I would say, you know, YouTube test. And, you know, I could just say, you know, initial, you know, start. And then I could say I would just want to use my custom function. To encrypt that. In fact, I can probably just look for, let's see, encrypt text, right? That's one we just created here. And we'll take start as its input, right? And then I will after that say, let us, you know, also run a custom function. And we will say it's, you know, decrypt Yeah, see, it was the, the searcher will find my custom functions as well, and this is going to be the result of func1. So if this thing is working properly, it should, you know, by giving me the result of func2, if I feed it good data at the beginning, I should be able to encrypt it, decrypt it, and maintain its security along the way. Yes, and if I want to find out what the encrypted text was along, along with that, I can say also give me the result of func1, which is— I can find out what both the encrypted payload was and also what I'd originally sent. Hopefully this gives you a bit of an introduction to how you can implement encryption in your own system, either by saying you want to store the data in your database encrypted, be able to send it to a third party, what have you. And one way or the other, you get to lock the door to your house. If you have— encryption is a very complicated topic. So if you have questions about it or thoughts about it, please drop them in the comments. And if you are working on things that relate to more encryption and this becoming something that's economically important for you, I'd encourage you also check us out over at StateChange, where we work on the hardest 5% of no-code, low-code problems and try to take things like this and make them into you know, from things that are big blockers into just these speed bumps that we all go through so that we're all able to ship the things that we need to ship. See you next










