http://www.perlmonks.org?node_id=11118636

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hello

I need to pass some params from a web form to a CGI script. Some params are passed by the user, others are "hard coded". My CGI strangely reads only the ones passed by the user, not the hard coded ones. Any idea?

<form action="/cgi-bin/delete.pl?key=gfgf&usr=rob" method="get" accept +-charset="UTF-8"> <div class="form-group"> <label for="id">&nbsp;Your token</label> <input id="id" name="token" type="text" class="form-control" reado +nly > </div> <div class="form-group"> <button name="submit" type="submit" class="btn btn-primary">Delete< +/button> </div> </form>

The CGI

#!/usr/bin/perl -T use warnings; use strict; use CGI qw(-utf8); use CGI::Carp qw(fatalsToBrowser); my $q = CGI->new(); my $token = $q->param('token'); my $key = $q->param('key'); my $usr = $q->param('usr'); print $q->header("text/html;charset=UTF-8"); print "Token: $token - Key: $key - Usr: $usr";

Replies are listed 'Best First'.
Re: Pass hard coded param CGI post
by perlfan (Vicar) on Jun 29, 2020 at 01:01 UTC
    >"hard coded"

    Back in my day, we called these hidden form elements.

    <form action="/cgi-bin/delete.pl" method="POST" accept-charset="UTF-8" +> <div class="form-group"> <label for="id">&nbsp;Your token</label> <input id="id" name="token" type="text" class="form-control" reado +nly > </div> <div class="form-group"> <input type="hidden" name="key" value="gfgf"> <input type="hidden" name="usr" value="rob"> <button name="submit" type="submit" class="btn btn-primary">Delete< +/button> </div> </form>

    Fortunately, we've learned a few things since then, like:

    • Send values via method=POST so you don't broadcast your values in the action URL even when using SSL/TLS
    • .cgi is a pain to maintain (use Dancer2 or Mojo)
    • this pattern of HTML form + cgi processor is inferior to using a modern JavaScript async call to send the data to a RESTful API endpoint (like /cgi-bin/rob/gfgf with method=DELETE, encoded as application/json

    To answer your question about what is happening? The browser is resetting the query part of your action (when method=GET, adding only the named form elements to the call to action and stupidly dropping what's in there. I am sure I had actions with query params in them once upon a time, and I don't recall the query string being fully replaced. But in any case, that's what's happening. I bet if you set method=POST and kept that action it would retain your query params. But use the <input type="hidden"..., that's what it's for.

      this pattern of HTML form + cgi processor is inferior to using a modern JavaScript async call to send the data to a RESTful API endpoint (like /cgi-bin/rob/gfgf with method=DELETE, encoded as application/json

      I do have to stop and take issue with this. A key principle on the Web is graceful degradation. Nearly all interactive browsers support forms, including such oddballs as Lynx and other curses-based browsers that do not require a graphical display. You can have a fancy JavaScript frontend, but you should still have basic forms — perhaps the CGI form processor translates the form submission to a RESTful request — for browsers that do not support JavaScript and users that choose not to run JavaScript.

      That last note is a legitimate choice no matter how many hipster Web developers whine about it — nearly all client-side Web security exploits in the modern era have depended 100% on JavaScript to run. Disabling JavaScript is very much legitimate security advice, unless of course you want your files ransomwared.

        nearly all client-side Web security exploits in the modern era have depended 100% on JavaScript to run[citation needed]

      Thank you for your help. Also thank you for the suggestions. I am writing a really basic application, so I ended up using the old CGI and HTML::Template. I am on a shared server which does not allow too much. It comes with CGI enabled and I think installing and using the Mojolicious framework is not possible. I had a look at the framework, and it seems to me that it would need quite an investment in time to set it up and learn it. I would be ready to use some Mojo modules to replace CGI. However, I could not find any way to perform in Mojo the same basic things I am doing in CGI, so to say, out of the box. Am I wrong?

        I am on a shared server which does not allow too much. It comes with CGI enabled and I think installing and using the Mojolicious framework is not possible.

        Yes, even you can use CPAN - Mojolicious is pure-Perl, with no non-core dependencies, which makes it especially suitable for installing in situations like these.

        I had a look at the framework, and it seems to me that it would need quite an investment in time to set it up and learn it.

        Well, for really simple CGI applications, sure, you could use e.g. CGI::Simple. But I've been using Mojolicious more and more recently, and I think it's good even for really simple stuff, and IMHO learning it is definitely worth the investment because you'll get so much more functionality out of a seemingly simple app. To get started, have a look at Mojolicious::Guides::Tutorial. I also have several examples on my scratchpad.

        However, I could not find any way to perform in Mojo the same basic things I am doing in CGI, so to say, out of the box.

        This is the equivalent to the code you showed:

        #!/usr/bin/perl use Mojolicious::Lite -signatures; get '/' => sub ($c) { $c->render(template => 'index'); } => 'index'; get '/delete' => sub ($c) { my $token = $c->param('token'); my $key = $c->param('key'); my $usr = $c->param('usr'); $c->render( text=>"Token: $token - Key: $key - Usr: $usr" ); } => 'delete'; app->start; __DATA__ @@ layouts/main.html.ep <!DOCTYPE html> <html> <head><title><%= title %></title></head> <body> %= content </body> </html> @@ index.html.ep % layout 'main', title => 'Hello, World!'; <div> %= form_for delete => ( method=>'get' ) => begin %= hidden_field key => 'gfgf' %= hidden_field usr => 'rob' <div class="form-group"> %= label_for id => 'Your token' %= text_field token=>'Default', class=>"form-control", id=>"id", r +eadonly=>undef </div> <div class="form-group"> %= submit_button 'Delete', class=>"btn btn-primary" </div> %= end </div>

        Though you should probably change get '/delete' to post '/delete' and method=>'get' to method=>'post'. This script will work both when started via e.g. morbo script.pl, and as a CGI script automatically.

        >Am I wrong?

        I am not super familiar with Mojo. And I do not think you're wrong. But you can use something like CGI::Application in your .cgi right away so that you can start organizing your code for a more "modern" paradigm. With some fiddling of the apache configuration (assuming), you can look at using actual routes via CGI::Application::Dispatch.

        It is true and relevant that changing your mindset is probably the hardest thing to do overall. I can see how on a shared hosting system where you don't have access apache configurations or to determine what is listening on port 80/443, that you would have the very real limitations you're describing. Go with what you know, but know that even in your environment you have some options.

      perlfan:.cgi is a pain to maintain (use Dancer2 or Mojo)

      silly statement as both mojo.cgi and dancer.cgi are both .cgi

        Care to explain the nuance or technicality you're using to sully my helpful and informative answer? I think in the context, it's clear what I am talking about.
Re: Pass hard coded param CGI post
by davebaker (Pilgrim) on Jun 29, 2020 at 16:20 UTC

    So if you have control over the form, then you probably want to change it to:

    <form action="/cgi-bin/delete.pl" method="post" accept-charset="UTF-8" +> <div class="form-group"> <label for="id">&nbsp;Your token</label> <input id="id" name="token" type="text" class="form-control"> <input name="key" value="gfgf" type="hidden"> <input name="usr" value="rob" type="hidden"> </div> <div class="form-group"> <button name="submit" type="submit" class="btn btn-primary">Delete< +/button> </div> </form>

    I think that's what perlfan suggested, but I am wanting to show in the above code that you'd definitely want to use the clean (no query string) URL to "post" to, plus the hidden fields shown above, because it's possible that your script, when it decodes the incoming parameters, won't do it correctly if there are query string ("get") parameters at the same time there are "post" parameters, even if the parameter names don't conflict. You might as well eliminate that risk, as shown.

    Also, do NOT use the "readonly" attribute. The user wouldn't be able to modify the contents of that field, meaning that the user will see a blank id field when it's displayed and won't be able to enter anything into it (what your form is calling a "token"). I've never seen the "readonly" attribute used in an HTML form, in 25 years of coding. Interesting.

      "Also, do NOT use the "readonly" attribute. The user wouldn't be able to modify the contents of that field"

      That is probably exactly why they wish this to be read only. Don't allow it to be altered, maintain the same aesthetics. It's unlikely they had this there by accident.

        I don't get it, though -- it seems to be a form in which the user is to enter a "token" in the input field named "id". If that field is merely to show an existing value for a token ("id") that's already on file, then I would think the "id" field that had been marked readonly would have a "value='<TMPL_VAR ID_ON_FILE>'" attribute as part of its inoput tag, where a value would be supplied by a program that's using the form as an template (HTML::Template) or something.

Re: Pass hard coded param CGI post
by Anonymous Monk on Jun 30, 2020 at 01:24 UTC
    A lot of form param processors distinguish GET params from POST params