 Okay, we should be live. I'm just plugging in my headset because I forgot to plug it in. So I should probably test that my headset's currently working. Welcome everybody to this Thursday Let's Code session. While you are joining, if you would like to get your local WordPress environment ready for the session, there is a new plugin to download. If you were here at last week's session, we're not going to be using the plugin code from last week. There's an updated plugin. So if you wouldn't mind, I'm going to paste it in the chat. If you wouldn't mind downloading that plugin and uninstalling the current one, deactivating, uninstalling the current one and installing the new one, I will walk through why there is a new one in a moment. If you weren't here for last week, then please just go ahead and download that plugin and you can start installing it on your local WordPress install if you want to code along with me today. I would also like to start before I introduce myself. I do see Tracy in the participants list. Tracy kindly offered to co-host with me today, but due to my own lack of getting things going early enough, I was not able to onboard Tracy for co-hosting today. So Tracy, I apologize for that, but hopefully we can work together in the future. Okay, so for those who don't know me, my name is Jonathan. I am from Cape Town in South Africa and it's starting to get into winter in Cape Town. So it is raining. So you'll see those of you who joined me usually on a Thursdays. I used to normally wear t-shirts, now I'm starting to wear some warm tops to keep warm in the office. I am a developer educator at Automatic and I'm sponsored to work with the training team, the WordPress training team. And the WordPress training team is a team of contributors and sponsored folks like myself who work on the learn.wordpress.org platform. I haven't linked to that in a while, so let me open that up in my window. Many of you will have been to this platform before you may have even discovered these online workshops through this platform. And this is sort of WordPress's official online learning platform. There are tutorials, there are lesson plans, there are courses. And then right at the bottom is the link to all the online workshops where you can join them and add them to your calendar and that kind of thing. So this is the site that I spent most of my time on. Okay, if you would like to let us know in the chat where you're from, that would be lovely, it's always nice to know where folks are from around in the world. While I'm doing that, we'll chat very briefly about what we're doing today. So today is a continuation of last week's workshop. Last week workshop was titled Common Vulnerabilities. And about halfway through the workshop, I realized that I wouldn't get to all of them. So I took the specifically the cross-site scripting, cross-site request forgery, sorry, vulnerability and split it out into its own workshop today. Welcome, Nordi from Peru, that's exciting. Welcome, Mary Beth from Manhattan. So today we're gonna be focusing specifically on the types of one or two of the types of cross-site request forgery vulnerabilities that could happen in a WordPress application or a plugin or a theme. And we're gonna be looking at how we can mitigate those vulnerabilities using something called nonces. And we'll chat about what nonces are in a minute. Before we get started, a few announcements. Again, welcome and thank you for joining me today. Please let me know if you can't see this announcement slide or any of the other shared screens that I'm sharing. I am sharing my desktop right now, so you should see my slides, specifically this announcement slide. But we did pick up that there's a possible issue with folks joining on Linux desktop. So if you can't see this, let me know and I'll just disable and re-enable the screen share. Then we are, as always, presenting in focus mode. We have had, unfortunately, another situation of Zoom bombing that happened yesterday. Those of you who might remember a contributor by the name of Mark Andrew, his nickname in the Zoom chat is usually Nomad Skateboarding because that's what he does. He skateboards all over America and lives in a medic lifestyle. He was presenting a workshop yesterday on I think it was developing a WordPress site and a mobile device. And unfortunately, some folks enabled their mic and just were loud or saying inappropriate things and sort of bombing the session. So that's why we present in focus mode. This is also a warning this could happen to us. So if anybody does jump on and start using inappropriate language, I'll apologize in advance, but hopefully we're all adults enough to just kind of mute it and ignore it and move on. I consider myself fairly thick skin, so those kind of things don't generally bother me, but I can only imagine for Mark as he was his first time doing this, it might have been quite disruptive. So that's why we present in focus mode. So focus mode means that I can see all of your video or right now I can just see all of your names because your video is not enabled. You are welcome to enable your video if you would like to, you don't have to, but if you want me to see your face, you're more than welcome to enable your video. As always, you are welcome. Hey, there's somebody you enabled the video, hi there. You are welcome to ask questions. You're welcome to unmute to ask questions or post them in the chat as we go through. Because I don't have a co-host with me today, all I would ask is that you just bear with me if I don't see your question immediately in the chat, because I have the chat window to my right here and while I'm presenting on screen, I can't always look over to the right, but I will check the chat. I do pause for sort of minutes, seconds, minutes, whatever to allow for questions and keep an eye on the chat and various other things. Okay, then as I mentioned before, make sure your local install is ready. I'm going to copy paste that link again for anybody who's joined recently. This is an updated version of last week's plug-in. So if you have last week's plug-in installed on your site store from last week's session, please deactivate it and install it and install this new plug-in. I just tested this plug-in before the session, so there should be no bugs in there. We fix, I actually fixed that bug that can't remember who picked it up last week, but I fixed that, that is set back that we picked up, so it shouldn't be a problem. Then most importantly, and I feel like I should behold this item, but I do tend to talk very fast when I get excited and I do tend to get excited about software development and developing with WordPress. So it's a bad combination. So if I'm ever going too fast, please just let me know. In the chat, tell me to stop, tell me to slow down. Ask me to pause on a screen, to copy paste code. I will go back to a screen if I've gone too fast with something. The goal of these sessions is for you to learn as much as you can from them, not for me to spend the whole day talking or the whole hour and a half talking. Naughty says, stop drinking too much coffee. You're absolutely correct on that one, Naughty. Unfortunately, the speed of which I speak is unrelated to my coffee drinking because I used to do it in high school and I didn't drink coffee in high school. It's just a, I think it's either a personality trait or just who I am. It's one of the reasons why I've never gone into public speaking really as a career. This is the first time I'm actually doing this as part of my job. So I make sure to warn people upfront. And then as always, the session is being recorded and will be posted to WordPress TV afterwards. So if you are wanting to watch this later or for whatever reason, you're not able to hang around, we will record this and post it afterwards. I usually post the link in the meetup chat the next day, which is when I publish it on WordPress TV. And then as I mentioned earlier, all the other WordPress focus content is on learn.wordpress.org. Okay, those are the announcements out of the way. I'm gonna, I'm going to follow Naughty's advice and have some water instead of coffee now. Our learning outcomes for today, we're going to spend a short time just recapping the vulnerabilities from the previous session. I actually have, and I'm going to share this in the chat, if you want to, if you're watching this now, this on WordPress TV later, or if you are wanting to watch the session we did last week, this is the link for it. In this one, we covered SQL injection, we covered cross-site scripting and we covered what was the other one. I've got it in my slides here. And we covered very basically the broken access control vulnerability. So if you're watching this on WordPress TV and you want to go back and watch that first you're more than welcome to, but we will review those very quickly now just before we move on. And then today we're going to be focusing specifically on what's known as cross-site request forgery or CSRF, or actually, I think they just call it CSRF. That's like the anachronome for CSRF. I don't know what it is with techies and anachronomes, but anyway, it's CSRF. So we're going to be looking at that and we're going to be looking at the way WordPress handles those with a thing called nonces. Now, if you're like me and you grew up with British English and nonce means something else. So I apologize if that word is a trigger to you. I used to work with a British colleague and he used to smile every time I used the word nonce because it means something else. Don't look it up, it's not worth it, but it just is what it is. And we're going to be looking at two ways we can prevent vulnerabilities by working with a form submission and how we can implement nonces on a form submission and then how we can implement nonces on asynchronous requests. And if there's time, I would also like to chat about something else around asynchronous requests. So we'll see how that goes. And then lastly, I'll just share some links on where you can learn more about these vulnerabilities and how to prevent them in your plugins and themes. The objectives list is literally just a copy of what I just said. It's more just for myself. There is still a bonus vulnerability in the plugin. So if you were here last week, I mentioned there was a bonus vulnerability. We'll see if we have time for that and chat about that briefly, but basically it'll just be these five, six things we'll be doing today. Okay, before we get started, does anybody have any questions, anything they need to know or want to know about everything we've just discussed? You can let me know in the chat otherwise, in about a 30 second break, we'll start getting things going. While, if you are thinking about questions, while you are doing that, I just wanted to share another little resource with you all. When we did the very first session on plugin security or WordPress security or whatever it was, I mentioned a tool that folks use to test web applications. And I remember the name of that tool, that tool is called Burp Suite. I don't know why the word Burp is used. It's probably again in an acronym, but there is a community edition of Burp Suite that you can download for free and install. And to tell you why I discovered, I'm gonna share this link in the chat. The reason I discovered this tool is because I was working for a company at the time and we had somebody who was a security pen tester who contacted us to say that they had discovered a possible, it was a broken, not a broken access control, but a privilege escalation vulnerability. In other words, a logged in user was able to access a different logged in users information. And so we asked them, well, how did you determine this? And they explained that they used this tool called Burp Suite. So I spent quite a bit of time learning to install Burp Suite and how to use it, verify that the vulnerability was there, fix the vulnerability, and then we paid them a back bounty, which is sort of the standard process in the web world. If somebody reports it ethically, in other words, they contact you directly and say, hey, we've discovered this and we're reporting this, there's typically a bounty. So some folks actually do that as a journey. And then you can set it up to sort of pause your web requests and then you can play around with the request data, the request method, all different kinds of things and see if you can hack your application. So I do recommend checking that out if you're somebody who is concerned about your web application security and you want to learn more. PortSwig are the company, I think is a security company. So I know they do have solutions for this kind of thing. They also have an academy where you can learn more, but they are also meet-up groups around the world that meet and teach the meet-up groups how to use these tools. So yeah, if security is something you're interested in, maybe I would have, if I could have gone back maybe 15 years and changed my career path, I would have loved to become a security pen tester and help companies find vulnerabilities in their code. So this would have been the kind of tool that I would have been very interested in back then, but I'm a bit old and weary to change career paths again, which is why I haven't looked into it. So I just wanted to share that with you. I recommend checking that out. Okay, let us get going. So I have already installed, let me move this out the way. I have already installed the current plugin on my local site. You can see it at the bottom here. It is badly coded plugin version 102 beta. So if you don't have 102 beta installed, you've got the wrong version and you will remember if you were here for last week sessions, it says never too old. Maybe, maybe I can become a security pen tester one day. You'll remember that we required a few pages for our plugin to work. We had a form submission page. Okay, let's open that page now. And inside of the form submission page, we added a short code. In the block editor, we use the short code plugin, short code block, sorry, not short code plugin. In the classic editor, you can just pop the short code in. The short code uses the WP learn form short code name, which I'm going to paste in the chat and then you can apply a class of either red or blue or you can leave the class out. And that is the bug that we found last week. So I'm actually going to do that to make sure that we fixed all of that. I'm just going to leave the class out. And if we now view that page, oh dear, it looks like the bug is still there. That's fine. I'm going to just have a look at that quickly. I thought I'd fix this. Let's have a look. Oh no, there's still there. That's annoying. I thought I'd ship this fixed with the code. Anyway, it does matter. It's not important to our missions today. So I'm going to pop that class back in again. Red and we'll update that and we'll view that. And if you use red as the class, it outputs a red border. If you use blue as the class, it outputs a blue border. And the form submission itself just allows you to submit a name and an email address. Then the other pages that we had, and if you haven't got these on your local site, you're welcome to create these now and I will give you some time to do so. There is a form success page, which contains any kind of content you want. The important part of this page is that the URL or the permalink slug for the page is either the term form-success-page or you change the slug in the plugin, which I'll show you in a second. For my purposes, I've just left it as the default form-success-page. And that will be the page that gets redirected to if the form-submission is successful, apologies. And then there is a form error page which follows the same syntax for the slug. So instead of form-success-page, it's form-error-page. And again, that gets used in the plugin. And this will be the page that somebody sees if the submission fails for whatever reason. So those are the three pages. If you need to create them, you're welcome to create them now. Creating the pages is not super, super required for this plugin code. You will be able to code along with me even if you don't have these pages, you'll just get a WordPress error because the redirection is not working. But if you need to create those pages, you're welcome to do so now. And then I'm going to start looking at the code itself. While you're creating those pages, let me go back to my form-success-page and just show you the process of the submission. Oh, that's the success page, not the submission page. There it is, one sec. So this is the submission page, I'm just gonna refresh this. And it's basically just an email, a name and email submission. So if we say Jane, and if we say Jane at gmail.com and we hit submit, it processes the form and then redirects to the form-success or form-error page. Okay. I'm going to say that if anybody needs me to stop because they're creating pages or they need to catch up, just give me a stop or a hold up or a chill out or relax or whatever in the chat. I'm going to move on otherwise, but I will pause if we need to, no problem there. The plugin code itself, let's go back to the plugin. We basically, at the top of the plugin, we have these constants that are set up and these are the pages that the plugin is redirecting to. So there we will see the form-success-page slug and the form-error-page slug. And then just very quickly, let's go through the plugin functionality. It creates a custom table to store the form-submissions. I don't want to dive into this too much. Then the next function is in-queuing the admin JavaScript, which I'll show you what that does in a second. It basically uses Ajax to delete form-submissions in the admin backend. I'll show you that page in a sec. There is an in-queue-scripts set of functionality which in-queues the red or the blue border for the front-end. Then there is the actual submission form shortcode. So it uses the add shortcode function to create the shortcode, sets up the callback. And this is where this should have fixed the bug. I don't know why it didn't. I'll check it in a second. So it's something important. And then it renders the form. So it renders some HTML to render the form with the different fields and it renders the class attribute for the styling. Then below that is the function to process the form data and somebody asked me last week, how is that happening? It's hooking into the WP action hook, checking if the WP learn form field from the form. If we scroll up, you'll see there's the hidden field, WP learn form. That's the name of the hidden field. If that field exists, in other words, it's been processed, then fire off this code. It takes the name and the email and inserts it into the database table and then redirects to either the success page or the error page. I think I've just realized something. I might be working with completely the wrong plugin here. Just give me one second. No, I'm not. It's fine. Okay. And then there we were submission, okay, submission. Then after the submission, that's all the front end stuff. Then I start with the back end code. To be honest, the way this plugin is structured is not ideal. I would have the front end code, maybe in one file, the back end code in another file, but I've got only one file just to make life easy. But below that is the action to create an admin submenu, which is the page that shows all the form submissions in the dashboard. The callback to show those submissions is below that. Then there is a function which just gets all the submissions from the database to be used in the callback above. It gets used over here at WP learn, get form submissions. Then right below all of that is the Ajax callback to handle the deletion functionality. So let me show you that deletion functionality right now very quickly. So if I was in the dashboard of the website and I go to the tools menu and I click on the WP learn admin page menu, this is what the dashboard page might look like. Obviously I would have preferred to have styled it better, but this is just for the purposes of the workshop. And here there is a delete button. The delete button contains the ID in the HTML. It fires off a JavaScript request, which passes the ID to the admin Ajax endpoint and will then delete that record from the database. It shows me a little bit of a message, which I can't see because the zoom window is over my screen and then refreshes the page and there the deletion happens. So that's the functionality of the form. Does anybody have any questions around any of that that we've just covered? Otherwise we'll start reviewing all the fixes we employed last week. I'm going to have another sip of water while we do that. All right, I'm not seeing any questions. Naughty says, all good. Thank you, Naughty, for that feedback. It's handy to know that folks are on board. Okay, so let's very quickly review the different things we fixed last week starting with, and we go back to my slides. We started with the SQL injection prevention. So the first thing that we did, if you were here last week, was in the form shortcode, in the learn form shortcode function. I'm trying to copy that there if you want to do a search for that in your editor. We, no, not there. Sorry, that's the next thing. Sorry, my apologies. In the processing of the form, in the WP learn maybe process form function. The first thing we did was we sanitize the inputs. So we use the sanitize text field function to sanitize the name. And we use the sanitize email function to sanitize the email. Text field is perfect for any kind of normal text or email specifically for email addresses. The other thing that we did was this, this section of code was using the WPDB query function and just running a standard SQL query, SQL query with the name of the email just inserted into that query string, which is another possible vulnerability because even though we're sanitizing the email and the name higher up, this code might change over time. And then we might somehow expose those fields to either somebody else or be used in a different way and those fields could have data ejected. So by using the, there's the insert function on the WPDB object that is used specifically for inserting data. There's also a function called prepare, which is to prepare a SQL query and it allows you to pass in the variables and then have them sanitize for maybe a different type of query. Maybe it's a different insert or a different delete or whatever. But that is the correct way to insert data into a custom table and make sure that data is safe. So that was the first thing we did. And then the second thing we did if we scroll all the way down to the Ajax callback. So this is the function that fires when the Ajax request is made to delete the form submission. It's a JavaScript request that submits a request to the admin Ajax.php file in the admin path in WordPress. We, first of all, we ensured that the ID value was being sanitized instead of using a sanitized function. We use PHP's type casting functionality and we put this int in brackets in front of the integer value. And basically what PHP then does is this must return a numeric value. So if you, if post ID, for example, I'm going to put this in a comment. If post ID was one, two, three, Bob, then casting at an integer, we just return one, two, three. If it was Bob, it would pass return as zero. And then what you should then do is you should then check where the ID is greater than zero. And if not, also do some kind of exiting, but we didn't do that. But basically that's what this single line of code does. So anytime you're working with integers that are being passed around, it's always a good idea to cast them to an integer value and then you know it'll always be the numeric. And then we also changed, again, we were using, if you have a look, and if you want to see what the original code for this looks like, I'm going to share that with you as well. This is the plugin security repository. It's the, what I call the original badly coded plugin. You'll see all the poor code there. It all works, but it's insecure. And I'm going to scroll that to the bottom and just zoom this in a little bit. Why is my Zoom? I need to use, no, that doesn't work. Some reason it's not working in my browser. So let's zoom in a bit here. There we go. You'll see down here, we were just using, we were setting up the SQL query. And then again, just running get results, so not really ideal for a deletion situation. So then we specifically use the WPDB delete function or delete method on the WPDB object. And again, that will take the ID and prepare it if necessary. And if any bad code gets in there somewhere, get rid of it, sanitize it and do what it needs to do. So those are the changes we made in terms of SQL injection prevention. Any questions around that before we move on? Otherwise, I will check my notes. Yeah, I think everybody's happy with that. So we'll move on to the cross-site scripting problems. So the things that we fixed there, I need to actually just check my notes here to remember because it's been a while. It's been a whole week Jonathan, come on. It wasn't that long ago. Oh, yes. In terms of cross-site scripting, whenever you see cross-site scripting, you must think about I need to escape my outputs. So anything that I'm rendering to the front end, I need to make sure that it's clean. Because if anything, any JavaScript or anything else gets ejected in that, the JavaScript gets run on the front end and then it opens up my site to either be attacked or be used to attack other sites. So the first thing that we fixed, if we go back to the top of the plugin and we saw it scrolling down and we go to the form shortcode. Here we go. Where the class is being output from the shortcode attributes. We changed it from just echoing the attributes class to actually wrapping that with the escape ESC underscore HCR function or escape attribute function. That is a function specific to WordPress that allows you to escape specifically HTML attributes. So if we were escaping maybe the ID or the class or any other attributes of an HTML element, we use escape attribute. So that was the first thing we did. And then the second thing we did, if we scroll down to the render admin page, that's a backend related vulnerability there, we were echoing the submission name, the submission email and the ID. And so to fix that, we used ESC HTML for the submission name and email. Because ESC HTML that says it in the documentation is specifically the correct function to use whenever you are escaping content that is wrapped by HTML elements. In this case, the element happens to be a table cell. So that's the perfect one to use. For the integer, we could have used escape attributes. Again, that would have been perfectly fine, but we could also cast it to an integer and the same thing will work. So those are the things that we did there. And if memory serves, those are the only places where we found those kinds of vulnerabilities. Yes. So those are the only changes we had to make there. And basically the trick or the tip that I suggested to folks was anywhere where you see the word echo, in other words, you're echoing something to a screen or you're using print in PHP. You should look for escaping that data. The other thing I wanted to mention, we didn't mention this last week, but I want to mention it now. And let's go over to the documentation and repress escaping data. So all of this is in the developer resources, common API security section of the handbook. So there are, for example, there is the escape HTML function. Let me zoom this in a third bit. But then there is also the sort of built-in, I'm trying to see if they're here in this list. I don't think they are. Here we go. So escape HTML underscore E, for example. So the way this works is it will, let me just check if it is this one. Yes. So escape, escape, ESC underscore HTML underscore E. Let me share this link in the chat just so you can see what I'm talking about here. It's like a special combination function. So if we have a look at the code of that function, which is down here, it takes the text, you can translate it, but then it echoes it for you. And then there's another one, which I think is E underscore escape HTML. So there's a couple of them. So it'll save you a whole bunch of echoes. So you can replace it with these custom escaping functions that also echo out the content. So I do recommend when you're working with your escaping functions, there is a much better list of, I think it was in the common vulnerabilities doc. There's a much longer list of these things. I don't know if it's here. Escaping data. Somewhere there's a list of all of the functions, but I don't see it here. It's probably somewhere in the codex. I'll see if I can find it. Here we go. Rather than use, here they talk about escaping with a localization, rather than using echo to output data, it's common to use the WordPress localization function, such as E underscore, blah, blah, blah. And then here it says these functions simply wrap a localization function inside an escaping function. So it's a great way of doing, there's the list escape HTML. Yeah, we were there. Okay. I apologize. So it's a good idea to know them as well. Yes. Let me pop this in here and I'll share that with everybody there. Okay. So it's a good idea to know the differences. I tend to, I tend to be a little bit more verbose. So I tend to write my code in the way that I've done it in this plugin where I use echo and then the escaping function. But that's just because I'm weird that way. There's nothing wrong with using these localization ones, which echo and escape at the same time. It's less code to write. But I tend to use the echo because I come from a pre WordPress world. I come from writing PHP code in HTML without any kind of framework or CMS. And that's how we, we output content, but it's always better and easier to use built-in functionality. So if you, if you can use these, these functions that echo and escape at the same time, I do recommend using them. I did use them when I worked full-time working in WordPress, but now I can get away with going back to my old habits. Okay. So it's one of those do do as I do as I say, not as I do. type of situations. And then the last thing we covered was, let me scroll down here, checking my notes was the broken access control. There was only really one place, one place where this was happening. You see, I start speeding up and I fumble over my words. And this was in the, right at the bottom here in the admin Ajax callback in the WP delete forms of mission hook. The callback linked into that. There was no chick. So there was no, there was no, the callback linked into that. There was no check. So this is an admin page, but there was no check whether the user that's clicking on the delete has the capability to actually delete that, that record. And the reason this is necessary is you might think, well, hang on. The person who is the admin because of the way you set up sub, the sub admin menu, you said manage options is the permissions for that sub menu page. Nobody would get there. Yes. But the problem is because the Ajax request could potentially be triggered by any user. So somebody using burp suite, for example, could be a user on your site. Could inspect your code somehow, get hold of a plugin that you're using or whatever, find a vulnerability like this and then just start hitting this form submission with ID numbers and start deleting form submissions. So whenever you're building this kind of custom functionality, it's always a good idea to make sure that the user is a logged in and B can do the thing it needs to do. And the current user can function is like the quickest and easiest way to do that because current user can. We'll both check for a logged in user. And if it doesn't find one, throw an error. And then if it is a logged in user, check against the permissions in this case, the manage options permission, which is specifically administrator only permission. And if it's a false, if you say it's, it can't do that, then it'll return a false, a response, a negative response. Sorry. So that was a nice, quick and easy one that we, that we had to fix there. Okay. That was a whole half an hour, maybe half an hour, but we'll get back to the recap of what we covered last week. Are there any questions around all of that? Anything anybody wants to know? If not, then, then give me a thumbs up or a good to go or an okay in the chat. And I'll wait for a couple of those before we, before we move on. My way of forcing you to interact with me. Okay. Now these good stewards. Good. Okay. Everybody seems to be happy today. That's great. All right. So the focus for today is specifically. The cross-site request forgery. Now, if I could just get my notes back up again. And it's actually kind of, it's kind of interesting that we ended up on this today because I tend to underestimate. Both the importance of this topic. And how complicated it can be to present it. I've actually given a plugin security talk a few times now. And every single time that the non-sys section, which is what is used in WordPress to prevent CSERF, I'm going to use, I'm just going to use the short hand now. It takes longer than I anticipate because it is quite an involved thing with SQL injection. It's just a case of making sure you're, you're sanitizing your inputs and preparing your statements. It's fairly simple to implement with. Cross-site, cross-site scripting. You just have to make sure your outputs are as clean. You're escaping your outputs and then you're good to go. With the user-facing stuff, you just implement current user can and you're pretty much good to go. There's not much more that you need to do for that. No problem, Marybeth. But with cross-site request forgery, it requires quite a bit of setup and implementation. And depending on the request that you're dealing with, requires different setup and different implementation. So we're going to cover two setups today. And if we have time, we'll chat about a third one. And then if we'll have time, I'd actually like to show you an easier way to work around a vulnerability where you don't even have to worry about, hopefully. So the term or the function or the piece of thing that you're going to use to print CSERF is to implement what's known as nonces in your WordPress plugins or your WordPress themes. A nonce is known as a number used once. In the Laravel application, it has a slightly different term that it uses. I think they call it a CSRF token, which is a lot more difficult to remember. Once you know that nonce means number used once and you have that term in your head as that, then you're usually good to go. But basically, if you think about a nonce, if you think about a number used once, when the page is set up, so when the form submission is first rendered or when the page is set up to submit the Ajax request, so before the actual request happens that is insecure, you effectively just create the number used once and it has a value. And the value is unique to that session. And then when you make the request, you pass that number used once, that nonce in the request. And when the request is received, you verify that number used once. And if that number used once is not verified, this is not a request that comes from our own website that we trust, and so therefore prevent execution. And so there are a few places that you need to think about this and you need to do this. As I mentioned, we're going to cover two today. The first one, and last week, I asked you to mention to me where these come from, but I've kind of given it away in the slides already. But the first one is in the form submission page. So if we go back to this form submission page here, this form submission, typically, we would be using on the front end of our website so that we want folks to be able to submit their data. And because we're submitting data, something could attempt to submit to that Ajax endpoint. So let me show you what I'm talking about. Let me open up my developer tools. If you've never worked with Ajax before, this might be useful and interesting to you. In fact, I want to go to network and I want to submit a request here quickly. I'm actually going to refresh this page so that we can see it happening. So there is the request of the form submission page. So it's a get request. It makes a request in the web server. It says, I want whatever is sitting at the form submission location. WordPress then checks, the shortcode runs, and it runs the page and it renders the content. So then the next request I'm going to make, I'm going to just close this one and I'm going to move, going to clear my current network activity, is going to be the form submission itself. So let's go with something like paul at gmail.com. And when I hit submit here, which I can't do because the zoom thing is over the way. So then you move that. There you will see, okay, it's already redirected. So you couldn't actually see it. Give me one second. I'm going to just make a change to my code quickly so that you can actually see the request. So let me go down here and I'm just going to stop the submission from redirecting. You're welcome to do this along with me if you would like to. So what I'm doing is I'm going into the into the maybe loan, WP learn maybe process form function. And just after the WPDB insert code, the first line of code is the if zero less than rows line of code there. I'm effectively going to comment this out. And what this will do is this will submit the form submission, but then continue execution and we'll end up back on the form page. No, sorry. Yes, that's exactly what happened. Sorry. It'll just stay on that page, which is what I wanted to do. Okay. So let me refresh this page. No, not the form success from submission. There we go. Refresh this page. And then we'll go. I don't think this might even work because the the submission is going to submit. And then that's fine. It should work. I'm just thinking of this out loud in my head as I go along. So let's do the form submission. There we go. Okay. It tries to redirect. So we don't actually see the post. It's not, it's not going to work, but effectively what happens is it makes a request of the form submission page and then it posts a bunch of data. And because this request, sorry, I'm just checking my notes in my head. Because this request could be made from anywhere. We want to make sure that, and the reason you would do this is because maybe you haven't got your validation set up or maybe some code has changed and you haven't fixed your code and maybe there's some SQL injections or whatever the case may be, but anything that can make a request that your plugin can make a request to external parties can also attempt to make a request to I could set up a, a burp suite or a postman request to the form submission page and try and pass in the right values and try and submit a form post to this, to this input. So we want to prevent that from happening. I apologize for the rambling there. I was getting myself a little bit lost, but that's effectively what we want to prevent. So with form submissions, I'm going to uncomment this code quickly now. With form submissions, the first thing we need to do, as we mentioned earlier, we need to set up the knots and we set up the nonce using a function, which I'm going to find for you and find the documentation for called WP nonce field. So let me show you that function. This is what it looks like. WP nonce field. I really, I really putting it in the chat Lisa. So what this does is this will create a hidden field on the form and I'll show you the form in a second. With based on a specific action and a specific name, and it'll generate the nonce for us. So that's the first step. We need to pay the form with the nonce field. So the way this works is, and I'm going to copy and paste this code for you as well. Once I've coded it is generally you would put your hidden fields inside of your form element. In fact, you'll see here, we've got a hidden field already, which is this learn form, learn form field that I'm using to check if the form is being submitted. So because WP nonce field is a PHP function, we need to use the PHP tags to be able to use this function. So this is what the PHP tags look like. And then this is what the code could look like. I'm going to pop this in the form in the page. And I'm also going to pop it in the chat. And then we can talk about. So what this will do is this will create a nonce based on the first parameter, which is the action. You'll see I've used the word action there. So we can remember this is the action. This action, this action field can be any string. You can call it my form nonce action. You can call it my action. You can call it whatever you want. You just remember what it is because we need to use it later. And then the second parameter is the field name. So as you, as you probably know, if you've worked with HTML form fields to be able to post the data, they need a name attributes. And generally the name attributes is that is the same as the property being sent. So for the, for the name field, I'm using a name attributes of name. I apologize for the redundancy there, but for the email address field, I'm using a name property of email. And then that's how when I process the form, I get the values. I get name from the post and email from the post. That's how I retrieve the data when the form has been posted. So what this is going to do for us is it's going to create the nonce and then create the field as well. The name of the field will be WP learn form nonce field. And the value will be whatever the nonce is. The action is stored somewhere else that only WordPress can access. And I'm pretty sure if I remember correctly, it's stored in a transient somewhere or in the database or something. So that when we verified, we look it up based on the action. And we will say verify the nonce with this action name, based on the value passed by this form name or this form field name. Okay. So let me show you what the field looks like first. So once, once you've inserted this field just underneath your hidden input using the PHP tags. And you render that form on your, on your form submission page, which we'll get to here. And then I'm going to inspect this in my developer tools. And I'm just going to zoom into developer tools quickly. So it's a little bit bigger. You will see there is our own learn WP learn form hidden field. That's from the code we wrote. And then just below that is another hidden field. The ID is WP learn form nonce field. That's the ID. The name is WP learn form nonce field, which is what we've specified. So it uses that for the ID and the name. And then it has the value, which is the knots that value and the number used once. You'll notice that action parameter is not anywhere on this, because if it was somebody could use the action and try and verify the nonce externally. We don't want that. But that's what it creates in the form field itself. But now obviously we need to verify this as being safe. And then we need to perform some kind of action. So now we move on to the, we find my code here. We move on to the, the code that processes this form data. And this is one of the reasons why my example code is hooking into the WP action because it's easier to do it that way. Really what you should do is have some other requests somewhere that processes it and then, and then render some content. I just try to keep it very, very simple. And so what you would do here is fairly high up in your request. You would want to check a wasn't non-sent. Is it the nonce we expect? So I'm doing that just below this is the form being posted because I don't want the verify nonce to happen. If the form wasn't posted, I'm going to just let execution happen. And it'll skip over all the other code anyway, because if the form wasn't posted, it'll just this, this top section of code here. If that form wasn't posted, it'll just continue and it'll return. But normally you would have it right at the top. So just below that, this is what the code typically might look like. So you would say something like this, the function is WP Verify nonce. I'm going to start by just pasting it in there. And I'm going to grab the documentation for that as well and share that with you. And I'll share the final PHP code as well once I'm done. But I want to sort of break it down step by step. So there's the Verify nonce documentation. And you'll see that it requires the nonce string and then the action. Okay. So the nonce string is the actual nonce value that is passed in. Sorry. The zoom controls have moved over my toolbar and I can't click. There we go. So that's this value. So that I would be able to get from the post request object. And I would use WP learn WP learn form nonce field as in the post array to get that value. So that's the first thing I need. So getting back to the code. It's here. Using the post array. This is a default thing that PHP sets up whenever a form is posted. And it'll set up the WP learn form nonce field because that's the hidden field on the form. So that is the first parameter for WP verify nonce. The second parameter, excuse me, is the action we specified when we created the nonce in the form field. And that's where I, I'll share a little bit of a secret here with the world. This is what confuses me every time because when I create the form field, the nonce field, I do the action first and then the field name. So when I verify, I tend to think I must do the action first and then the field value, but no, it's the other way around. So remember this, if you're using the hidden field, it's action then name. When you verify its value, then action. Okay. So verify nonce will return a true or a false value if the nonce is verified. So we want to be able to use that true or false value. So it could be as simple as something like saying if, if this is true, if that's true, if that's true, if that's true, if that's true, and if that's true, and wrapping that if statement around WP verify nonce. And we say, if, if this is true. Then go ahead and continue processing. A quicker, sort of a quicker easier way to do this would be similar to the way I'm doing this at the top here. And we do what we call early returning or early guarding. So we say, if not, which means if that response to verify nonce is false. exit out of this execution and doesn't attempt to process anything further. That could be as simple as it is. But there's a problem with this code. And the problem with this code is, what if the nonce hasn't been posted in the form? I should actually return even before that. So what I ideally would want to do would be something like this. I would want to say, just slightly higher up. I might want to say something like this. If not, is set the form field. Just the same way as I did the not is set WP learn form. So here I am saying, if the form field isn't even set in the first place, don't even do the verification, just return immediately. And that's a nice quick way of exiting and making sure nothing else happens. So that would be the first thing I would do is I would check, was the field that I set up passed in the submission? If it wasn't just end execution right here and they don't go any further. If it has been passed, great. We can move on to the next step and we can say, if it is verified, or sorry, if it's not verified, then again, exit execution, don't continue. Otherwise, if this is true and that is true, then we're happy and we're saying, yes, this is a request. I'm going to copy past this into the chat so long so that folks can grab it while I'm talking. We will say, yes, this is a request we're happy with. We can now continue to go ahead and process this data. Then last but not least, I just want to mention that it is also possible to combine these two and you can do something like this. You can say, if not is set, the field, or those two lines mean, or it's not verified, then we could return or even better, we could redirect to the error page. So if the non-season set, because the plugin has got the error page set up, redirect to the error page. Now, the reason you might want to do that, I want to share some information here with you. I'm going to paste this code in the chat as well. If somebody is trying to hack your site, if you didn't have the redirection in place, they're going to see, oh, there's no kind of redirection somewhere else. Maybe if I keep digging, I'll see an error message, the error message might expose some data, little unknown thing about not so much WordPress but PHP in general. If you haven't got your PHP security configured correctly, certain errors can actually or used to be able to, I don't think they can anymore, but they used to be able to expose your MySQL username and password. This was a number of years ago. I think it has been fixed since then, but not having some action, some redirection to some page that displays to a user if things aren't being verified is also a possible security vulnerability because you might just throw like a default PHP error or a default WordPress error, which actually gives the attacker more information. So it's always a good idea to actually redirect to a specific error message. You might think it's only going to be read by a human, but it could also be read by, and then it would just be, oh, something went wrong with the form and there's no further information that you're giving away. So it's always a good idea to redirect to something, redirect to the 404 if you want to, page not found, that's also perfectly acceptable. That's why the 404 page generally doesn't exist. You can just redirect to that. I saw once, it wasn't an application that I was working on, but I saw once where an application, a Laravel application, they hadn't set up the default 404 page. And every time an error happened on the page, you saw the sort of the debugging information presented on screen, which is a security vulnerability. So it's always a good idea to redirect to some human readable page and therefore not expose further information about what you have set up. Okay, so I'm going to leave the top two, I'm gonna take the top two out and leave the bottom one just because that is slightly better. And I'm going to take a break there before we continue and see if there are any other questions around any of that, what we've just covered in terms of form submissions. Okay, I don't seem to have questions, so we're going to move on. The other place we have a possible C-surf vulnerability is in the AJAX request. So I'm gonna just pop right down to the bottom here. So while we are checking that there's a valid user, what we're not doing is checking, is it coming from this website? Now let's chat about the theory behind this for a second. Having the current user can check could be considered enough because if an external party sent requests to this AJAX endpoint, they typically wouldn't be logged in as a user and it would fail. The problem though is you might have built a website, excuse me, that has different logged in users and somebody's user credentials might get leaked somewhere. And somebody might be able to, and because of the fact that user logins are effectively just hitting the login page on any application, not just WordPress, any application, it's finding the login URL, hitting submit with the username and password and then either storing a cookie or storing some kind of session and then you're an authenticated user. And now this is a little bit of a tangent, so I apologize, but this is where some folks think, oh, the way to fix this is to hide your login URL. There's these plugins that change the login URL to something else or they append a variable or whatever. At the end of the day, that's still a valid URL even if it's not WP-login, even if it's admin slash bob slash access slash user, you still have to access that login on a web browser which means it's still public. So it's still is open to possible vulnerabilities. So whenever you're doing any kind of asynchronous requests using Ajax, you should really be checking for CISO vulnerabilities. You should be checking that it's coming from the trusted site, your site that you're building or that your plugin or your theme is installed on and you would use nonces to do that. Okay, so just as we set up a nonce for the form, we need to set up a nonce for the Ajax request. Now, the only place that we can set this up because the way Ajax works, and I'll pop over to the admin JavaScript file, the way Ajax typically works is we create, in this case, it's a jQuery request. You could be using something like Axios which is a JavaScript library for creating requests. It's going to create a post request and it's going to require a URL to be able to make that post request too. In this instance, the URL just happens to be the WordPress Ajax URL which if we go back to the PHP file and we scroll right up to the top here, we've specified here as the admin URL with the word adminajax.php. If you've never seen that before, that's the default way that WordPress handles Ajax requests. I'll show you what it looks like on the front end in a second but we need to set up the nonce to pass it in with this request data here. So this is the request data. We're passing in the action and the ID to make that request happen. We need to pass a nonce into this request data. So let me show you what that looks like on the front end before we set up the nonce. I'm going to find the, what I need to look for is the WP Learn admin, sorry, the WP Learn Ajax object in my HTML. So I'm going to switch back over here and in my elements, I'm going to search for WP Learn Ajax. Oh wait, sorry, it's in the, it's not on the front end, it's in the admin dashboard. So let me pop on over to the dashboard. It's in this Learn admin page. So let's find that. Where are my tools? Here we go, tools, Learn admin page. So it's when the deletion is happening which is an Ajax request. And if we'd now search for this, there it is. I'm going to make this a little bit bigger so we can see it on screen. So there, what that, what, so to understand this, if you've never seen this before, what this code does, this localized script passing in the admin script handler and the Learn Ajax name and this array, it essentially passes, it ends up creating a variable called WP Learn Ajax. And that variable is an object and inside of that object, the first field is the Ajax URL. And that URL is the path to the local site, WP slash WP admin slash admin Ajax.php. So that is the file or the URL that's going to accept the Ajax request. It accepts the data we send it and then it fires off the Ajax hook which is the hook we've specified right at the bottom of this code here. So it fires off this hook here because we've set this up as delete form submission, it will create and fire the WP Ajax delete form submission hook because we've hooked this call back into that hook, then this code runs and that's how Ajax works in WordPress. I'm planning a tutorial on that for the future. So if that wasn't fully understandable to you, it is coming, don't worry. So we need to now get a non-syn here somehow. So to do that, we're going to update this WP Learn Ajax object which is in the right at the top here in the learn in queue script function. So it's where localized script happens. There's WP Learn admin, WP Learn Ajax, Array, Ajax URL. We're going to pass the non-syn over here. So I'm just going to call it non-syn. It doesn't matter too much what it's called. And then I need to actually create this non-syn pass it through. So to do that, we can use the WP Create non-syn functions. I'm going to just copy some code quickly here and show you what that looks like and then I'll share links with you. So there's WP Create non-syn. You'll see that it just requires one parameter which is essentially the action. In this case, WP Learn Ajax non-syn we could have called it action. I'm just calling it Ajax non-syn because I'm going to use it for all my Ajaxes and that's all we need to pass. So let me get that documentation and share it in the chat with you. So there it is WP Ajax non-syn. As you can see the first parameter is the action parameter. It's the same as the form. It doesn't require a field because it's not creating a form field. Then we need to set that variable up in the object so there it is over there and I'll send you the updated code for that in the chat. So that's what it looks like. If we now refresh that admin page and we have a look for that Learn Ajax code again let me open up DevTools. Let me find the elements and let me search for Ajax I think it was. I'm going to find it. No, it wasn't that which one was it? WP Learn Ajax. Yeah, why is it not finding it? Have I broken this? It's very possible I've broken this. Ajax non-syn, Ajax non-syn. Let me just take this out and see what I've done here. Don't you love it when the person presenting breaks things on live on? That's largely annoying. I mess with the JavaScript. It's very possible I missed the JavaScript. It doesn't look like I have. I wonder if the create non-syn correct strokes. Just resetting everything back to what was. Sorry, my fat fingers are breaking things here. Okay, let's try that again. Ah, that's frustrating. It was working and then it stopped working. It's always fun. Let me see if this deletes. Okay, maybe I'm searching incorrectly. Oh, I am. I'm searching in the wrong screen. What a silly person. That was fun. There it is. Okay, dear. DevTools let me down there. I was searching in the wrong screen. Okay, let's try that again. So let's do, no. So let's leave that as it was. And let's go back. And now this is, my mistake is now recorded for all time, which is beautiful. So we'll create the nonce. There we go. And we'll pass it in. I could just edit this out before I upload it. And then nobody has to see it. Just you folks. Okay, let's put that in there. All right, that should now be there. So let's refresh. And then it's searching the correct place. There it is. Okay, dear, dear Amy. I was searching in styles, not in the elements. Okay, so there's the Ajax object and you'll see, I've spelt it as none, but there is the nonce there. Let's fix that problem. Not none, but nonce. But you see, it doesn't actually matter. Whatever I call it doesn't actually matter because I'm just gonna reuse it in the JavaScript. So it's actually maybe even a good idea to not call it a nonce, to call it a token or a key so that it's not obvious what you're using it for. I'm just using nonce today because it makes it easier for you folks to see what I'm doing. But it's probably a good idea to maybe use something else, like a token or something. But now that the nonce is there, what this means is this is available in the WP Learn Ajax object so we can pass it into our Ajax request. So if we go over to the JavaScript, we can now, at this point here, we can add a new element or a new property to the data being posted. Now, the important thing to understand here is that when you pass this nonce, you need to pass it in a very specific format. And if you have a look at the documentation that I pasted earlier that create nonce documentation, I think it does actually specify this in the documentation. So let's just scroll down and find it. It doesn't actually specify, but if you have a look at this example here, you can use the create nonce functionality and you can pass things around. Is this the one that we used? Yeah, it is. I think it might be in the plugin handbook. Let me find that for you. Let me do a search. Yeah, I'm gonna actually talk about the specific property. Oh, yes, that's right. So it's in the function that we're going to use to check the nonce and it's in the check Ajax referrer. And you'll see that here it says the key to check for the nonce in request. If false, the return base will be evaluated for Ajax nonce and or WP nonce in that order. So you can actually specify a specific nonce to be checked. And I'm gonna paste this in the chat, no problem Stuart. But if you don't specify a specific nonce to be checked, then you must use either Ajax nonce or WP nonce. So I tend to prefer to use Ajax nonce just because I'm using Ajax requests. So in my Ajax code, very specifically, I always use specifically Ajax underscore Ajax underscore nonce. And then I'm going to pass in the nonce value and that is currently sitting. If I show you on the front end, if we go back to here, that's currently sitting in the learn Ajax object in the nonce property. So then what that means is just like I can refer to the learn Ajax object here and Ajax URL. I can do the same thing and I can say for the semicolon WP learn Ajax, that's the object. And then I can search for the nonce property. So like that. I'm going to, and then I've got to put in a comma or it's invalid. I'm going to copy this into the chat as well. So you can see that change. But that's me passing the nonce from the object to the request. So that'll now get passed to the request. So I set up the nonce using create nonce and then adding it to the learn Ajax object using localized script. I can define any value for this property if I want to. I've just chosen to call it nonce here today. Then in my JavaScript environment, I need to set it up in the request. So when I'm creating the post request to the Ajax URL, I need to specify either Ajax underscore Ajax underscore nonce or underscore WP underscore nonce, one of those two. Then I need to do the check. Now, the nice thing about this being an admin interface, it means I can use this check Ajax refer a function that we mentioned earlier. And the cool thing about checking the Ajax referer is that it will do a whole bunch of checks and then show a error to the user if there's a problem. And because it's in the admin interface, we don't have to worry too much about showing incorrect information, but you could also do a similar thing like checking it and then returning as need be. So if you have a look at this code, effectively what it does is it gets the request. So this is the code for check Ajax referer. It gets the query or Ajax nonce or WP nonce, whichever one and then it simply uses WP very, yeah, WP verify nonce. So it uses the same code we were using earlier. And then based on that, it'll then do certain things. It'll check Ajax referer and dye and whatever else. It does a whole bunch of other things. So you could do this yourself. You could take this code and say result is WP verify nonce, the nonce being the post request, in this case, the action being Ajax nonce and you could specify your own or you could just use something like check Ajax referer. I think it should actually, I just realized something, small correction, I think it's actually check admin referer for admin, yeah, it's check admin referer, there we go. So let me send you that one from another admin page with security, sorry, I am wrong, sorry. Check admin referer you use when you are redirecting from one admin page to another, my apologies. Check Ajax referer is the one you use specifically for Ajax requests. So as you mentioned in the first plugin security, use the right function for the right functionality. So Ajax referer is correct. So all we then have to do in our code, if we go to the Ajax callback, we can then say, right, check the Ajax referer and then it will show correct information and do various things if that Ajax referer fails and that's all you have to worry about. So now your nonce is in place, let's check if the form still works with all that code in place. So if I go to my form first, I'm gonna just, my form shouldn't be affected but I'm going to just check it first. So let's go back to pages and let's go and view form submission again and let's go with my sons were playing the name game this morning in the car. So I've got a whole bunch of names in my head. If you don't know the name game, I'm happy to explain it to you in a second but let's go with Nathan. So Nathan submitted, that seems to have worked. Let's go back to the dashboard. Let's go back to the admin page that we created. There is Nathan, now the deletion should work as well. We were doing the non-verifications and the form has been deleted and there we go. It is all still working but at least now it's working with extra security and you can test how all this stuff works by for example, let's actually do this. Let's remove the check Ajax referer and see what happens when we submit that. So let me refresh this page and we should see that it might say deleted. Okay, it actually does delete. So there's a bug somewhere there. It shouldn't actually have done that. It should have thrown me some kind of error. That's interesting. Oh, that's why I wasn't implementing things correctly. Was I? WP learn Ajax nuns. That's interesting. Oh, I removed the check. So all I did was I removed the check which meant everything else is gonna continue. So what I should have done was left the check but removed the actual nuns. That would have tested if it works. You see, I can't even test my code properly. So let's remove the nuns. That will be the correct check. So let's take out the nuns. There we go. And let's try and submit that page. So now if we go here and we try and delete this nothing happens. And if I open up developer tools and we switch to the console we should see some kind of error which is exactly what we want. So now we know the verification is in place. If the nuns isn't passed it's going to fail. Let's check if passing in incorrect nuns works. So let's leave the nuns in and this is a good way to test these things when you're coding with us. Don't just assume it's gonna work. Remember that those of you who were here for the very first plugin security we did always be checking even your own code. And we'll talk about unit test one day as well how we can test this automatically. But let's create a nuns. So here we've got WP learn HX nuns, right? So let us verify for a different nuns action. So let's say we want to verify for this. So that's correct code but the two actions don't match up. So now we should also see some kind of error. So let's do that. And I'm starting to talk fast because this gets me excited. So let's delete that. And again, we see a forbidden error. So now we know our code is doing what it should do. So even there we created a valid nuns but we didn't check for that nuns. So now we know we need to fix that bug in our code. Okay. Any questions around how to verify those nuns form submissions, HX requests, anything around that while I slow myself down and have a refreshing sip of water. Okay. There don't seem to be any questions. The last, there's two more things that I wanna quickly discuss. The first one is a better way of doing your HX requests. We're not gonna dive too deep into this today but those of you who were here for my rest API workshops I've started to record some of those workshops as tutorials on WordPress. And one of the things that I mentioned is that specifically in interacting with WordPress rest API I'm gonna share both the using with and the interacting with the WordPress rest API videos in the chat because I think those are good resources to know. I will do my best to update these in my slides before I upload this to WordPress TV. So hopefully if you're watching this on WordPress TV these links will be in my slides as well. But in the interacting with WordPress rest API video I talk about how you can use the WordPress rest API to replace your HX requests and you use something called a backbone JS client for the JavaScript site and you use the rest API for the receiving of the data. And the great thing about going with that route is you don't then have to worry about the nuns checking because the backbone client and the rest API already worked together and do those checks. So if you're using backbone JS and your plugins to submit your data in your backend the nuns is on to problem for you. So that is another way that you can prevent those vulnerabilities. In fact, that is my top tip for today. If you are going to be building a plugin or a theme that needs to interact with the WordPress database in an asynchronous way in JavaScript I would rather recommend you use the WordPress rest API and the backbone JS client to do that because that will make your code less, sorry more secure by default because you're using these built-in tools. That's top tip number one. And then lastly, I mentioned about the bonus security vulnerability. If anybody went and checked the code that I showed last week, it was hidden in there but essentially it's this, I don't think I'm doing let me see if I can find where I am doing it incorrectly but I think I actually fixed it by accident while I was preparing this plugin. I'm just gonna, here we go. So in this nonce field code we were looking at earlier I'm using WP redirect to redirect to the error page. And that itself is another vulnerability because WP redirect should only be used when redirecting outside of your WordPress site. If you are redirecting internally there is a special function which I'm using down here called WP safe redirect. And you should use safe redirect to redirect to pages internally into your site because using safe redirect prevents those redirections from also being hijacked. If you're redirecting externally for whatever reason maybe there's a link on your page and you want to in PHP direct to user somewhere externally we don't mind if somebody hijacks an external URL because it's not gonna expose anything but if it's any kind of internal redirection especially if you're redirecting in an admin environment you're creating settings pages and on submission you want to redirect somewhere else rather use WP safe redirect than WP redirect. I will share the link to that documentation in the chat as well. That's just a little tip to remember if you're ever working with plugins that need to redirect anywhere else use WP safety redirect for internal local redirections and it says checks whether the location is using an allowed host. So it's similar to nonces it checks whether where I'm redirecting to is a safe host that I'm happy to use. Okay, that is my bit for today. I hope you have enjoyed that. I hope you have learned how nonces work and how you can use them. Does anybody have any other questions before we wrap up today? Otherwise we will call it a day and go off and enjoy our Thursdays. I would also mention while folks if anybody is posting any questions all of these workshops that I'm doing this year are going to be turned into tutorials as well. I think I shared, I think I've already redirected back from that. So my plan for 2023, my sort of personal goal for 2023 is that all workshops become tutorials so that anybody who wants to see the abbreviated shortened version of this content can go and check it out on learn.wordpress.org. Stuart says, is there any WP specific documentation on the backbone JS and API press? Stuart, there is indeed. If you go to, I'll actually take you there now. If you go to developer.wordpress.org, for some reason, ThemeJSON has been remembered in my history from last year so many times. I'm gonna paste that in the chat. And then if you go down to the common API section and you click on Utilize APIs, there is an entire section dedicated to, where is it now? Hang on. To the REST API, which then takes you to the REST API handbook. It's an entire handbook on its own. You can actually also, I'm gonna go back to the developer.wordpress.org homepage. There is actually right at the bottom here, there's a whole section on the homepage dedicated to the REST API handbook. So you scroll down to REST API, click Make Applications, and it talks you through how all this works. The use in the REST API section is what I focused my first set of tutorials and workshops on. And then there's also the extending the REST API where you can do various things. But everything you should need to know is in this documentation. So if you wanna go that route, I highly recommend reading through the docs. I am also busy preparing all the tutorials around that content. And eventually I'm gonna create a little short course based on that content. So if you wanna do that, that's also an option, but that's unfortunately only in the future. Okay. Any other questions before we wrap up today? I would like to thank you for joining me. I know that plugin security or theme security or WordPress security is often something that folks tend to forget about sometimes. I would challenge you to always be thinking about security, always be thinking about how your code is being used, how your code could be used for nefarious means. And whenever you're working with data, inputting data, accessing data, Lisa gets your answer, your question in a second. Think about security and think about where things can go. REST in API stands for representational state transfer. It's just specifically an architectural style. For the, I would say kind of easy to read and understand explanation of that, I would just go to the Wikipedia article on REST API, on representational, representate, I can't even say the word. Representational state transfer. And really it just means how the data is passed back and forth and sort of the guidelines around REST. But that's a whole different topic. Cool. Thank you all so much for joining me today. Next week, we're going to be looking at user roles and capabilities and how we can use those in our plugins and themes and how they work. So please do join me for that. Otherwise enjoy the rest of your Thursday and when you get there, enjoy your weekend. Thanks very much.