Security issues with sending back sensitive info after failed form submission - c#

I have a ASP.NET Core application where I have to collect a handful of information about an applicant. One of the fields is SSN.
I have a GET action in my controller that displays the fields of the form. It looks like this:
[HttpGet]
[Route("~/applicant/")]
public IActionResult Applicant(int id, Guid guid)
{
var model = CreateEmptyViewModel();
return View(model);
}
I then have a POST action in my controller that checks if the form submission is valid and moves on or reloads the form accordingly.
[HttpPost]
[Route("~/post-applicant")]
public IActionResult PostApplicant(MyViewModel model)
{
if (model == null) throw new ArgumentNullException(nameof(model));
if (ModelState.IsValid)
{
// code that moves on
}
else
{
TempData["Error"] = "The form is incomplete or corrections are needed.";
return View(nameof(Applicant), model); // reloads form with fields filled out
}
}
My view model looks like this:
public class MyViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string SSN { get; set; }
}
All properties in MyViewModel are required. If the user decides to supply SSN but not first name, the form submission will fail and the form will be reloaded.
Are there any security related ramifications for reloading a form with recently typed sensitive information? Is there a better way to do what I am doing?

Encrypting your requests and response via SSL is good first step, but it is not a solution in itself. HTTPS traffic can be intercepted and man-in-the-middle attacks are a real concern, particular on insecure networks like public wifi. Unless you can ensure that all your users are running latest and greatest platforms with all security patches applied and are always connected to secure networks directly or via a secure VPN, then you can't just brush off PII as fine because it's HTTPS. Even then, there's always zero-day exploits you can never account for.
Long and short, you should treat all sensitive PII as sacrosanct at all times and never transfer it over the line unless you have to. Initial collection is one such occasion, but that doesn't mean it should come back over the line on error. It's perfectly okay to make a user re-enter sensitive information again, and most users tend to understand why they have to. For example, if you make an error with a credit card payment form, your credit card number doesn't come back filled in already - that would be a severe violation of PCI.
So, in your controller action, you should do the following:
ModelState.Remove("SSN");
model.SSN = null;
return View(model);
That said, there's probably worse PII to potentially leak at this point. Thanks to Equifax, virtually everyone's SSN is already public. Still, it's always good to think about what data you're sending back and forth.

As long as you are using HTTPS, then there are no security implications.
The only way you could make it better is by doing the POST as an AJAX request in the background. Then you only need to return the error message if there is one. The only real benefit is a better user experience since they don't need to wait for a full page refresh. There's an example of that here: https://stackoverflow.com/a/6960586/1202807
But the way you're doing it is fine too.

Related

How to pass fragile data e.g connection string from one controller to another

Im new to asp.net core and I was trying to develop an online SQL database manager, that will work on any SQL database, after passing: ServerAddress, Login, Password and DatabaseType (my ConnectionInformation model).
Something like SSMS but online.
I want to pass my ConnectionInformation model from Login controller to Database controller.
Redirecting to action uses query string which exposes all of my data.
TempData only accepts strings and converting my model to json isn't the most elegant way to solve this problem.
Login Controller:
public class LoginController : Controller
{
private readonly ILoginLogic _loginLogic;
public LoginController(ILoginLogic loginLogic)
{
_loginLogic = loginLogic;
}
[HttpGet]
public IActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index(ConnectionInformationViewModel connectionViewModel)
{
if (!ModelState.IsValid)
return View();
ConnectionInformation connection = Mapper.Mapper.ConnectionInformationMapper(connectionViewModel);
var connectionSuccess = _loginLogic.ConnectToDatabase(connection);
if (connectionSuccess)
return RedirectToAction("Index", "Database", connection);
else
return View(); // TODO: Return view with error or handle it in js
}
}
Database Controller:
public class DatabaseController : Controller
{
private readonly IDatabaseLogic _databaseLogic;
public DatabaseController(IDatabaseLogic databaseLogic)
{
_databaseLogic = databaseLogic;
}
public IActionResult Index(ConnectionInformation connection)
{
var databases = _databaseLogic.GetDatabases(connection);
return View(databases);
}
}
Prehaps my approach is totally wrong. My main goal is to check if I can connect to database, and if I can I want to perform query operations on that Database untill user logs out.
Well, simply, you have to persist the information some way. In that regard, there's a number of options. You could persist it in local storage on the client and actually pass it back with each further request. That works better for SPA-style apps, where you're pretty much doing everything via AJAX, though. Another technically client-side storage mechanism would be setting a session cookie with the posted connection information. Here I'm talking about explicitly setting and reading from a cookie with a "session" lifetime, not using sessions.
Or you can can actually use a true session, i.e. Session. TempData is basically just Session anyways, but here it would be inappropriate as you'd then need to ensure that the TempData is kept every time it's accessed or it won't survive the next request. If you're doing that, then you might as well just use Session and not have to worry about it.
Serialization is pretty much required no matter what you do. There's no way to persist an actual C# object instance, so you're either going to have to write it to a relational store like a database or serialize it to JSON.
One alternate solution, which avoids having to persist the actual connection information is to basically create your own connection pool. This will require a singleton-scoped class with a ConcurrentDictionary ivar and likely the use of SemaphoreSlim to lock during reads and writes of that dictionary so that you don't create and orphan connections. Then, you'd just need to assign the key to their particular connection in the dictionary client somehow, such as via Session or a cookie. This is actually a little more secure as well, as you're not persisting the database connection info past the initial post, but you might end up exhausting the available server connections if there's too many simultaneous users. Of course, that could potentially be an issue regardless. You'll also need some policy for eviction of connections. It's not the easiest setup.
Personally, I'd just stick with using Session on this one. It's secure enough, as long as you take the standard session hijacking prevention measures, and it's simple to implement. If you are building a SPA-style app, then I'd stick with local storage, as that's going to be imminently better keeping the info client-side to begin with, but it requires a bit more plumbing that way.

Altering database based on user id

I really looked, googled, this site, for a few days now, tried a bunch of different things, and I can't find an answer.
so, I'm trying to create a web application that will display client information after purchase. I'm using VS2012 express and C#, and I decided to use MVC4, mostly because there was a tutorial on ASP.NET that came pretty close to what I was looking to do. Any comments on my choices is not requested but also not unwelcome.
So the admin will enter all sales information at the end of each day. We record client phone numbers as account numbers in our sales protocol, so my thought was, to keep it simple, to just use the clients phone number as a login to the web application as well. Also, that way, when a client logs into the site to view the database, the database would filter automatically so that the particular client could only see their transactions.
The tutorial I followed is here.
I figured out that this is the point where the filter needs to be applied, but i'm having a lot of trouble doing so.
The controller is named "MainController"
The database is named "Main", table is "Mains"
"AccountNumber" is the field in the db that should match the Current User Id
public ActionResult Index()
{
return View(db.Mains.ToList());
}
As I understand it, I have to place [InitializeSimpleMembership] above, then grab the UserId, then define it as the filter.
First of all, you should decide one of these way:
1) keeping login and user info in a separate table and let the SimpleMembership(SM) does its default jobs.
2) Using an existing table so store users info and tell the SM which table is for.
Approach One:
To handle this approach, all you need is that you create users manually(as you do, I think) and add an extra line to the action method which is responsible of creating customers:
[HttpPost]
public ActionResult Create(Customer model)
{
if (ModelState.IsValid)
{
db.Customers.Add(model);
try
{
db.SaveChanges();
// here it is ...
WebSecurity.CreateUserAndAccount(model.Phone, model.Password);
}
catch
{
insertError = true;
}
// .. Other codes ...
}
Now, your customers can simply login to the site with their phone no. as username and that password.
And to retrieve items related to a specific user - which is currently logged into site - simply use the following query:
public ActionResult Index()
{
return View(db.Mains.Where(m => m.AccountNumber == User.Identity.Name)
.ToList());
}
If you also need approach two, tell me to update my answer and put it here

What are the responsibilities of the components in an MVC pattern for a simple login

I'm trying to understand the MVC pattern and I get the general idea that the Model is responsible for maintaining the state, the View is responsible for displaying the Model and the Controller is responsible for modifying the Model and calling the appropriate View(s). I wanted to try and implement a simple ASP.NET MVC login page that uses OpenID in order to get some understanding of how it all works.
I've downloaded DotNetOpenAuth-3.4.6 and I was looking through the samples, specifically their MVC sample. Unfortunately, the sample doesn't actually have a model, only a controller:
namespace OpenIdRelyingPartyMvc.Controllers
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.RelyingParty;
public class UserController : Controller
{
private static OpenIdRelyingParty openid = new OpenIdRelyingParty();
public ActionResult Index()
{
if (!User.Identity.IsAuthenticated)
{
Response.Redirect("~/User/Login?ReturnUrl=Index");
}
return View("Index");
}
public ActionResult Logout()
{
FormsAuthentication.SignOut();
return Redirect("~/Home");
}
public ActionResult Login()
{
// Stage 1: display login form to user
return View("Login");
}
[ValidateInput(false)]
public ActionResult Authenticate(string returnUrl)
{
var response = openid.GetResponse();
if (response == null)
{
// Stage 2: user submitting Identifier
Identifier id;
if (Identifier.TryParse(Request.Form["openid_identifier"], out id))
{
try
{
return openid.CreateRequest(Request.Form["openid_identifier"]).RedirectingResponse.AsActionResult();
}
catch (ProtocolException ex)
{
ViewData["Message"] = ex.Message;
return View("Login");
}
}
else
{
ViewData["Message"] = "Invalid identifier";
return View("Login");
}
}
else
{
// Stage 3: OpenID Provider sending assertion response
switch (response.Status)
{
case AuthenticationStatus.Authenticated:
Session["FriendlyIdentifier"] = response.FriendlyIdentifierForDisplay;
FormsAuthentication.SetAuthCookie(response.ClaimedIdentifier, false);
if (!string.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
case AuthenticationStatus.Canceled:
ViewData["Message"] = "Canceled at provider";
return View("Login");
case AuthenticationStatus.Failed:
ViewData["Message"] = response.Exception.Message;
return View("Login");
}
}
return new EmptyResult();
}
}
}
Perhaps the sample is too simple to actually involve a model, since all of the state information is contained within the Cookie. Subsequently, I've implemented a simple database which has a single Users table:
Users
+ user_id
+ open_id
+ last_login
I presume that I would need a LoginModel:
public class LogInModel
{
[Required]
[DisplayName("OpenID")]
public string OpenID { get; set; }
}
A DisplayModel:
public class DisplayModel
{
[DisplayName("User ID")]
public string UserID{ get; set; }
[DisplayName("OpenID")]
public string OpenID { get; set; }
[DisplayName("Last Login")]
public DateTime LastLogin{ get; set; }
}
Additionally, I may need a ModifyModel, but the DisplayModel can be reused and possibly renamed to UserModel (to properly reflect the use of the model).
So now I have several questions:
Is the controller responsible for verifying user input or is that done when the input is passed to the model (i.e. calling Identifier.TryParse on the openid_identifier)?
I want to allow the user login, change their information (i.e. the user_id) and view their information (i.e. user_id, open_id and last_login). How many models do I need (ModifyModel, DisplayModel and LogInModel)?
What is the lifetime of a model? Does the model exist somewhere statically or is it just created by the controller and passed to the view?
Would it be better to add the database as a model instead of making the above models?
1) It could be yes, but a better approach would be to use Data Annotations on the ViewModel.
2) One model will do. A model should represent an overall object, in this case a "User". If the information required for each View differs greatly, then seperate them out into View Models.
3) Not sure what you mean. MVC (and ASP.NET in general) is based on the HTTP protocol, and is thus stateless. So when you hit a URL, a Controller is assigned, then objects are newed up as the code requires it - this includes a database connection. Then when the request is finished, everything is gone (managed resources anyway). Try not to get confused with the word "model". It's not a physical entity, rather an area of your programming model.
4) Generally, your "model" is your Repository/DAL/ORM, which wraps your underlying database, and represents your domain model. Your View's shouldn't be concerned with the domain. That is the job of your Controller/Model. Your View should work with what it needs, and no more. This is why as a rule of thumb, never bind directly to an ORM model, use a ViewModel.
Edit - in response to questions in comments:
I'm sorry... for 1, Data Annotations suggest the validation is performed on the model, but OpenId.Identifier also provides some functions that can validate input (e.g. TryParse), so would it be more consistent to do all of the validation on the model or is the "place" of validation usually not that strict?
Put the Data Annotations on the ViewModel which are representations of the Model created to make life easier for the View. Data Annotations should not be put on your actual model entities (Entity Framework, L2SQL, etc). Data Annotations should be used for input validation (password comparing, length of characters, phone numbers, email addresses, etc). Business Validation should be done in the domain. I would say that OpenId is a service and not part of the domain. If they have some validation functions, you could wrap those calls in custom data annotations and place them on your ViewModel. That would be a clean and consistent approach.
For 3, I agree that the HTTP protocol is stateless, but if I understand correctly cookies are one way to maintain a state and the model provides an alternate way.
Your right, cookies are one way to maintain state. A common use for this is the Forms Authentication ticket. Another one is session. TempData uses Session (in a smart way - automatic ejection). The model is not an "alternate way" - it does not maintain state. Will talk more about that below.
In application programming a state (i.e. an object) is usually persistent throughout the lifetime of the application, so if I were to create a Model in a console app it would exist for as long as there is a reference to it. So when you say that the "objects are newed," does that mean that the Model object is already instantiated (like in a console app) and I just "update" it or does that mean that I construct a new Model?
In a console app - your right. As long as the console application is running, the objects are alive. But in a ASP.NET MVC Web Application, the "parent" is a ASP.NET Worker Thread, assigned when a request comes in. All of the required objects (controller, database connection, repository, domain objects) are "children" of this thread, if that makes sense. Once that thread is gone, so is all the related objects.
There is no "magic instantiation" of the model, again - the Model is an overall view/representation of your domain, which usually consists of the domain model (entities, business logic) and the repository.
The one exception is "model-binding". When you submit a Form to an [HttpPost] action that is strongly-typed to a "model" (should be a ViewModel). ASP.NET MVC (via reflection) will construct this "model" based on the fields in the HTTP POST.
When you do things like "UpdateModel", all that is doing is updating an object you supply with what came into the action method via the model binding. No actual database is being updated.
Don't know what else i can say. You seem to be having confusion about the "model". May i suggest you grab a copy of Steven Sanderson's Pro ASP.NET MVC 2 Framework book. It's fantastic, explains everything from the ground up - in simple terms, then ramps up the pace so your an expert by the end of the book.

What is a good method for preventing a user from submitting a form twice?

I have a purchase page and I don't want the user to be able to refresh the page and resubmit the form once they get to the 'order complete' page because it automatically sets them up in our system via database values and charges their card via paypal (only want these to happen ONCE)... I have seen some sites that say 'Don't hit refresh or you will get charged twice!' but that is pretty lame to leave it open to possibility, what's a good way to only allow it to be submitted once or prevent them from refreshing, etc?
PS: I saw a few similar questions: PHP: Stop a Form from being accidentally reprocessed when Back is pressed and How do I stop the Back and Refresh buttons from resubmitting my form? but found no satisfactory answer... an ASP.NET MVC specific answer would be ideal too if there is a mechanism for this.
EDIT: Once they click submit it POSTS to my controller and then the controller does some magic and then returns a view with an order complete message, but if I click refresh on my browser it does the whole 'do you want to resend this form?' that is bad...
The standard solution to this is the POST/REDIRECT/GET pattern. This pattern can be implemented using pretty much any web development platform. You would typically:
Validate submission after POST
if it fails re-render the original entry form with validation errors displayed
if it succeeds, REDIRECT to a confirmation page, or page where you re-display the input - this is the GET part
since the last action was a GET, if the user refreshes at this point, there is no form re-submission to occur.
I 100% agree with RedFilter's generic answer, but wanted to post some relevant code for ASP.NET MVC specifically.
You can use the Post/Redirect/Get (PRG) Pattern to solve the double postback problem.
Here's an graphical illustration of the problem:
What happens is when the user hits refresh, the browser attempts to resubmit the last request it made. If the last request was a post, the browser will attempt to do that.
Most browsers know that this isn't typically what the user wants to do, so will automatically ask:
Chrome -
The page that you're looking for used information that you entered.
Returning to that page might cause any action you took to be repeated.
Do you want to continue?
Firefox - To display this page, Firefox must send information that will repeat any action (such as a search or order confirmation) that was performed earlier.
Safari -
Are you sure you want to send a form again?
To reopen this page Safari must resend a form. This might result in duplicate purchases, comments, or other actions.
Internet Explorer -
To display the webpage again, the web browser needs to
resend the information you've previously submitted.
If you were making a purchase, you should click Cancel to
avoid a duplicate transaction. Otherwise, click Retry to display
the webpage again.
But the PRG pattern helps avoid this altogether by sending the client a redirect message so when the page finally appears, the last request the browser executed was a GET request for the new resource.
Here's a great article on PRG that provides an implementation of the pattern for MVC. It's important to note that you only want to resort to a redirect when an non-idempotent action is performed on the server. In other words, if you have a valid model and have actually persisted the data in some way, then it's important to ensure the request isn't accidentally submitted twice. But if the model is invalid, the current page and model should be returned so the user can make any necessary modifications.
Here's an example Controller:
[HttpGet]
public ActionResult Edit(int id) {
var model = new EditModel();
//...
return View(model);
}
[HttpPost]
public ActionResult Edit(EditModel model) {
if (ModelState.IsValid) {
product = repository.SaveOrUpdate(model);
return RedirectToAction("Details", new { id = product.Id });
}
return View(model);
}
[HttpGet]
public ActionResult Details(int id) {
var model = new DetailModel();
//...
return View(model);
}
While serving up the order confirmation page you can set a token that you also store in the DB/Cache. At the first instance of order confirmation, check for this token's existence and clear the token. If implemented with thread safety, you will not be able to submit the order twice.
This is just one of the many approaches possible.
Note that the PRG pattern does not completely guard against multiple form submissions, as multiple post requests can be fired off even before a single redirect has taken place - this can lead to your form submissions not being idempotent.
Do take note of the answer that has been provided here, which provides a workaround to this issue, which I quote here for convenience:
If you make use of a hidden anti-forgery token in your form (as you
should), you can cache the anti-forgery token on first submit and
remove the token from cache if required, or expire the cached entry
after set amount of time.
You will then be able to check with each request against the cache
whether the specific form has been submitted and reject it if it has.
You don't need to generate your own GUID as this is already being done
when generating the anti-forgery token.
Give each visitor's form a unique ID when the page is first loaded. Note the ID when the form is submitted. Once a form has been submitted with that ID, don't allow any further requests using it. If they click refresh, the same ID will be sent.
Simply do a redirect from the page that does all the nasty stuff to the "Thank you for your order" page. Having done that, the user can hit refresh as many times as he likes.
If you doesn't like redirect the user to other page, then by using my way you dose not need Post/Redirect/Get (PRG) Pattern and the user remain on the current page without fear of the negative effects of re-submitting of the form!
I use a TempData item and a Hidden field (a property in the ViewModel of the form) to keep a same Guid in both sides (Server/Client) and it is my sign to detect if the form is Resubmitting by refresh or not.
Final face of the codes looks like very short and simple:
Action:
[HttpPost]
public virtual ActionResult Order(OrderViewModel vModel)
{
if (this.IsResubmit(vModel)) // << Check Resubmit
{
ViewBag.ErrorMsg = "Form is Resubmitting";
}
else
{
// .... Post codes here without any changes...
}
this.PreventResubmit(vModel);// << Fill TempData & ViewModel PreventResubmit Property
return View(vModel)
}
In View:
#if (!string.IsNullOrEmpty(ViewBag.ErrorMsg))
{
<div>ViewBag.ErrorMsg</div>
}
#using (Html.BeginForm(...)){
#Html.HiddenFor(x=>x.PreventResubmit) // << Put this Hidden Field in the form
// Others codes of the form without any changes
}
In View Model:
public class OrderViewModel: NoResubmitAbstract // << Inherit from NoResubmitAbstract
{
// Without any changes!
}
What do you think?
I make it simple by writing 2 class:
NoResubmitAbstract abstract class
ControllerExtentions static class (An Extension class for System.Web.Mvc.ControllerBase)
ControllerExtentions:
public static class ControllerExtentions
{
[NonAction]
public static bool IsResubmit (this System.Web.Mvc.ControllerBase controller, NoResubmitAbstract vModel)
{
return (Guid)controller.TempData["PreventResubmit"]!= vModel.PreventResubmit;
}
[NonAction]
public static void PreventResubmit(this System.Web.Mvc.ControllerBase controller, params NoResubmitAbstract[] vModels)
{
var preventResubmitGuid = Guid.NewGuid();
controller.TempData["PreventResubmit"] = preventResubmitGuid ;
foreach (var vm in vModels)
{
vm.SetPreventResubmit(preventResubmitGuid);
}
}
}
NoResubmitAbstract:
public abstract class NoResubmitAbstract
{
public Guid PreventResubmit { get; set; }
public void SetPreventResubmit(Guid prs)
{
PreventResubmit = prs;
}
}
Just put them in your MVC project and run it... ;)
Off the top of my head, generate a System.Guid in a hidden field on the GET request of the page and associate it with your checkout/payment. Simply check for it and display a message saying 'Payment already processed.' or such.
Kazi Manzur Rashid wrote about this (together with other asp.net mvc best-practices). He suggests using two filters to handle data transfer between the POST and the follwing GET using TempData.

ASP.NET MVC: Using GET and POST in the same method

Here's the scenario: when a new user registers to our web site, we want to send an email to verify that the user owns the email address. In the email there's a link to a page that will do the verification, something like this:
http://www.mysite.com/account/verify/token
The verify method looks like this:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Verify(Nullable<Guid> id)
{
// tries to get the user based on the verification code
if (ValidId(id))
{
// if the id is correct, update user data in the model and redirect
return RedirectToAction("Index", "Dashboard");
}
else
{
// redirects the user to the verify view
return View("Verify");
}
}
The "Verify" view is simply a textbox with a button, so the user can enter the verification code manually (the user can get to this page from the site, and might prefer just to copy-paste the code). When the user clicks on the button, I want to do the same thing that my GET method does; so I ended up with something like this:
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult Verify(Nullable<Guid> id) { ... }
I have a couple of issues with this code (which works, but...):
Is it OK to have a GET and POST method? Or is there a better way to handle this scenario?
I'm modifying data in a GET method (if the id is correct, I update the user data to reflect that it's verified) and that's a big NO NO ... still, I want the user to just be able to click on the link and verify the token. Is there a better way to achieve this?
Thanks
I personally wouldn't bother with the AcceptVerbs attribute. (** See note below) You could then combine this into one action, which could respond as needed. (Showing some untested code below.) The reason I'm adding an answer instead of just a comment is that I wanted to recommend you add one more branch to your logic, to handle a failed code (i.e., to present an error message).
public ActionResult Verify(Nullable<Guid> id)
{
if (!id.HasValue)
{
// nothing was submitted
ViewData["message"] = "Please enter your ID and press Submit";
return View("Verify");
}
if (!ValidId(id))
{
// something was submitted, but wasn't valid
ViewData["message"] = "ID is invalid or incomplete. Pleaes check your speeling";
return View("Verify");
}
// must be valid
return RedirectToAction("Index", "Dashboard");
}
You then of course could display <%=ViewData["message"]%> in your Verify view. This is of course just a simple example.
** OK, here is my note RE: not bothering with the AcceptVerbs attribute:
In your scenario you could also just choose to make your form's method GET instead of POST. Because you're already "taking action" and modifying state on the handy link your users click on, I wouldn't see any difference. I'm just mentioning this to be thorough even though I'd personally opt for my previous recommendation.
Good luck!
I'm modifying data in a GET method ... and that's a big NO NO
I wouldn't say it's always a big no no. HTTP says that the GET method "SHOULD" be "safe", that is it SHOULD have no effect other than information retrieval. In this case, I think it's reasonable to stretch the definition of "safe" to mean that it doesn't have any harmful side effects, and indeed the only possible side effect your verification link can have is a desirable one.
The other property that the GET method is supposed to have is idempotence: if the user clicks the verification link multiple times, it's the same as if they clicked it just once. Hopefully you have this property, since a verification link is generated with a single-use code.
I have to say it is rare to find somebody so concerned with using the proper HTTP verb. I don't believe the original intention of the HTTP spec was to confine all data editing submissions to POST and all retrievals to GET. I think what you're doing is just fine.
if you are worried about it then what is wrong with this?
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Verify()
{
return View("Verify");
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Verify(Guid? id)
{
if (ValidId(id))
{
return RedirectToAction("Index", "Dashboard");
}
return View("Verify");
}

Categories

Resources