0

Given:

I have created Hash and Captcha classes. Hash creates form tokens. Captcha uses a Graphics class to create an image. A custom session service wrapper class is used to handle the $_SESSION superglobal data structure.

Scenario:

I use a dependency injection container. Therefore, I want to know where to define the instantiation of Captcha and Hash for publicly accessible HTML forms.

Hypothesis 1: You should inject Hash and Captcha into a child of Model because accessing $_SESSION is required to store (upon HTTP request) and to validate (upon HTTP reponse) CSRF tokens and CAPTCHA answers. A view should never access session data structures.

Hypothesis 2: You should inject Hash and Captcha into a child of View because generating CSRF tokens and CAPTCHAs is actually part of a View's presentation logic, even though validation of them occurs in the Model. Accessing session data structures, directly or indirectly, from a View is permitted.

Hypothesis number one seems like the answer, but I want to be sure.

Abstract Example:

$session = new Session(); // A custom wrapper class
$hash = new Hash();
$graphics = new Graphics();
$captcha = new Captcha($graphics);


$model = new ContactModel($session, $hash, $captcha);

or

$view = new ContactView($session, $hash, $captcha);
Anthony Rutledge
  • 6,980
  • 2
  • 39
  • 44

2 Answers2

2

Note #1: I think you should read through this post. I will emphasize a core part of this answer: "The model is not a class or any single object"

Note #2: IMO this problem is something inherent to web MVC implementations. The first part is how I would go about this problem; the following describes conceptually why this problem exists.

My answer is: neither.

The MVC architecture is divided into 2 layers: the model layer and the presentation layer. A service object (these reside within the model layer) should instantiate/save pertinent captcha information to the client state. Since this is likely a web application, the presentation layer -- specifically a view object -- will need to request this client state from the model layer. Whether this happens through a service object or a presentation-specific data access object specially-tailored for client state responses is dependent on your implementation & best fit. It is here, within the view, where I would save the client state to the session via another data access object or a (Cookie/Session)Response abstraction*. In this way, the view doesn't "access" the session data structure, but rather utilizes a component (the presentation-layer DAO or (Cookie/Session)Response abstraction*) to do this work. Though we don't think of cookies/sessions as presentation ("they aren't visual"), the presentation layer is responsible for responses, which cookies/sessions are nothing short of.

It is difficult for me to speculate how this would go in your framework specifically, since there seems to be a conflation of the model layer. Your ContactModel would certainly want the hash/captcha to save the client state. Your view would need neither of these, but an additional mechanism to "respond" the client state to the HTTP request in the form of a cookie/session.

* Symfony is generally helpful for this. I use both the presentation-layer DAO and a Symfony HttpFoundation object, which may be overboard.

Conceptually: sessions as state vs. response

The fundamental problem you're tackling here is specific to web MVC implementations. Presentation layer in web MVC processes a "request" (delegated to the controller) and presents a "response" (decided upon by the view). In HTTP, cookies and sessions are sent with requests and altered in responses. This is embedded in the HTTP request/response nature.

If we put the session/cookie saving in the model layer, we are making our model layer dependent on HTTP and allowing it to "respond" to requests. Even if we abstract the superglobals and "inject" abstractions (like Symfony cookie/session objects), there is inherent dependence on cookies/sessions which are request/response implementations. You can do this all in the model layer, but I prefer not to. In other words, though the model is responsible for keeping track of states, the cookie/session state is actually a response to a request.

"But this is leaky logic since you are saving state in the presentation layer"

I think you can look at all methods as being somewhat "leaky" and this is just the most cohesive way. Sure we are "saving" state in presentation layer if you look at cookie/session as state. If you look at them as response, which they truly are in the eyes of MVC, then this is not leaky at all. I would argue that sending a response (saving the session/cookie state) in the model layer is worse.

Community
  • 1
  • 1
jeremy
  • 9,965
  • 4
  • 39
  • 59
  • Thank you for being so invested in providing your answer, insights, and opinions. In my case, the token and the CAPTCHA (base64 encoded) resolve down to strings that are ultimately embedded into an HTML template. These strings could be passed as data to the View (as other data is). I know you argue against tracking request/response state in the Model, but is that looking at the blackboard too closely? One could say this is all under the umbrella of business logic-- a constraint. – Anthony Rutledge Oct 22 '18 at 14:07
  • I think the View accesses these strings and then responds with (1) the correct http body text and (2) the correct http headers (the cookies/sessions). – jeremy Oct 22 '18 at 14:13
  • What I mean is that the ultimate purpose of form tokens and CAPTCHA response values are to prevent normal business logic from executing, unless these values are validated. On first time page draw, Model says, "Here View, take these strings. Put them in the right spot. That's all you need to know." – Anthony Rutledge Oct 22 '18 at 14:14
  • I dont see why this is leaking business logic in the Model. It is the models job to respond with data dependent on the business model. I dont see anything wrong in your latest comment, other than the fact that captcha is part of your business. – jeremy Oct 22 '18 at 14:15
  • So you are for the Model? One advantage of generating the form tokens and CAPTCHA strings inside the Model is that the entire life cycle exists within one sphere of influence: generation and validation. Obviously, hiding/viewing them is left to the View. Generating form tokens and CAPTCHA values should only occur on first time draw and on validation failures. If you keep this bit in the Model, you can generate the token and/or CAPTCHA value right at the point where need is determined. – Anthony Rutledge Oct 22 '18 at 14:22
  • i guess your question is unclear to me. in both of your hypotheses the model is what is responsible for captcha. i understood your question mostly as where to save captcha related state – jeremy Oct 22 '18 at 14:25
  • a fundamental communcation here is our understanding of “Model.”. I am using the MVC definition where the model is a layer filled with business objects like services, entities, DAO. the model is a model of your business, not the page the user lands on. of course captcha is part of the business model – jeremy Oct 22 '18 at 14:26
  • We are in accord about the "Model." We have been from the beginning. – Anthony Rutledge Oct 22 '18 at 14:33
0

Answer: You should generate CSRF tokens and CAPTCHA values in the "Model".

Reason: The purpose of these values is to serve as a constraint on your business logic, not simply to display values or controls in the "View". If the "View" generated form tokens and CAPTCHA challenge values, it would have to communicate them back to the "Model" (one way or another). This goes against the flow.

While hypothesis number two is feasible, it gives duties to the View that it does not complete in full. Specifically, a View would never be responsible for validating form tokens or CAPTCHA answers.

A View could, in a dummy like fashion, insert form tokens and CAPTCHA challenges into an HTML form template (for example). This is true, however now the "View" would be responsible for saving the answers to persistent storage of some kind, while the "Model" would only retrieve and validate them.

Thus, session storage would be acting like a backside channel between the "View" and the "Model", and therefore coupling them (even if using some kind of session service wrapper). The "View" should not be passing data do the "Model", but that is effectively what is happening if the "View" is allowed generate form tokens and CAPTCHA answer values.

That is my answer. I welcome your comments and criticisms.

Anthony Rutledge
  • 6,980
  • 2
  • 39
  • 44
  • i agree with what you say here but i am confused by the session storage component. the model should never be saving anything to sessions simply to allow the view to read it (i know you agree w this). this isnt what sessions are for; doing it this way would just be the same as defining a global. i didnt realize you were asking about the validity of using a global to transfer data between model layer & view objects — is it? – jeremy Oct 22 '18 at 16:03
  • Agreed. Upon an initial HTTP request, the "Model" would pass a form token and CAPTCHA challenge value to the "View" by standard message passing (along with other data needing to be displayed / transmitted). The "Model" should be responsible for both the input to, and output from, session storage. – Anthony Rutledge Oct 22 '18 at 16:20
  • "Model should be responsible for both the input to, and output from, session storage" is where we disagree. Session storage is related to a stateful client. Client interactions go through the controller and are informed by the view. IMO the controller should pass tokens/whatever to the service objects (model layer) only after reading them from session storage. View objects will set sessions (because they are embedded in HTTP response), however they do not necessarily know what this is; merely that it is part of the response. They know how to handle this response. I hope my answer gets at this – jeremy Oct 22 '18 at 16:28
  • @jeremy It could be that you have different experiences than I, as I have shunned all frameworks and gone to the root--object oriented design patterns. That said, allowing session storage to be a **bridge** between the "View' and "Model" (going in this direction) would break the Observer Pattern--where the "Model" is being observed, an hence updated in the "View". What you effectively suggest is having the "View" (during HTTP request) inform the "Model" in a delayed fashion (from a previous request, via session storage) during a form submission.so that the "Model" can test submitted values. – Anthony Rutledge Oct 22 '18 at 16:42
  • No, I do not suggest this. In no way should session storage be a bridge between View and Model. Please tell me where you see me saying this. I directly contradict this idea above : "this isnt what sessions are for; doing it this way would just be the same as defining a global." Similarly, in no way should the View inform the Model of the session state. Never. I also never say this. I similarly use "object oriented design patterns" and no built in frameworks other than components from Symfony, Auryn, etc... – jeremy Oct 22 '18 at 16:44
  • Think of it like this, where API serves as access point between the presentation and model layer: `Request (POST data, GET data, COOKIES) <--> Controller ===> API <=== View <--> Response (body, COOKIES, SESSION)`. Model should keep track of client state, but should not keep track of client sessions (in my web MVC), as this would be leaking request/response into the service/DA layer – jeremy Oct 22 '18 at 16:47
  • @jeremy "View objects will set sessions ...", but there is no need for them do so in the server-side sense of the idea of sessions. If you mean that an HTTP response may need to set cookies, this is true. However, the determination that they must be sent, and the values that must be sent, should not start from the View. – Anthony Rutledge Oct 22 '18 at 16:50
  • I feel as though you are not reading what I say. Yes, View objects set the cookie by sending it in the response. But read on: "View objects will set sessions (because they are embedded in HTTP response), **however they do not necessarily know what this is; merely that it is part of the response.**" – jeremy Oct 22 '18 at 16:51
  • I am also confused on how your Model is inherently object-oriented, since it seems to be based on transactions, but whatever .. – jeremy Oct 22 '18 at 16:55
  • @jeremy This is not as complicated as you have made it out to be, or as you your understanding informs you that it is. Two values need to be saved and checked later on (token, CAPTACH). The generation, saving, and checking can happen exclusively in the Model layer. That's it. Model can pass this data to the View like any other data. The method off of the Controller only needs to serve as context in which the Model passes data to the View. My original question was based on the idea you could accomplish generation and display by the View, but not validation Hypothesis 1 is more correct. – Anthony Rutledge Oct 22 '18 at 17:06
  • To clarify, I think the "saving" part, if you are doing this in the model layer to *user sessions* (i.e., cookies), is a logic leak. Anyways – jeremy Oct 22 '18 at 17:08
  • I say this gently and with a warm smile, but I do not think using cookies for form tokens or CAPTCHA answers would make sense. The form token (hence, the name) would be embedded inside of an `` field, and checked against a value stored in **server-side session storage** (meaning, it is never transmitted to the client). The correct CAPTCHA answer is never transmitted to the client, either. The only cookie in play in this limited scenario would be the session cookie. – Anthony Rutledge Oct 22 '18 at 17:15