Buy

Guess what? You could deploy this code right now. Sure - we have a lot more to talk about - like subscriptions & discounts - but the system is ready.

Oh, but there's just one thing that you cannot forget to do. And that's to force https to be used on your checkout page.

Right now, there is no little lock icon in my browser - this page is not secure. Of course it's not a problem now because I'm just coding locally.

But on production, different story. Even though you're not handling credit card information, Stripe does submit that token to our server. If that submit happens over a non-https connection, that's a security risk: there could be somebody in the middle reading that token. Regardless of what they might or might not be able to do with that, we need to avoid this.

There are a lot of ways to force HTTPs, but let me show you my favorite in Symfony. In OrderController, right above checkoutAction(), this @Route annotation is what defines the URL to this page. At the end of this, add a new option called schemes set to two curly braces and a set of double-quotes with https inside:

70 lines src/AppBundle/Controller/OrderController.php
... lines 1 - 11
class OrderController extends BaseController
{
... lines 14 - 27
/**
* @Route("/checkout", name="order_checkout", schemes={"https"})
... line 30
*/
public function checkoutAction(Request $request)
{
... lines 34 - 68
}
}

OK, go back and refresh! Cool! Symfony automatically redirects me to https. Life is good.

No HTTPS in dev Please!

Wait, life is not good. I hate needing to setup SSL certificates on my local machine. I actually have one setup already, but other developers might not. That's a huge pain for them... for no benefit.

Fortunately, there's a trick. Replace https, with %secure_channel%:

71 lines src/AppBundle/Controller/OrderController.php
... lines 1 - 11
class OrderController extends BaseController
{
... lines 14 - 27
/**
* @Route("/checkout", name="order_checkout", schemes={"%secure_channel%"})
... line 30
*/
public function checkoutAction(Request $request)
{
... lines 34 - 68
}
}

This syntax is referencing a parameter in Symfony, so basically a configuration variable. Open parameters.yml, add a new secure_channel parameter and set it to http:

25 lines app/config/parameters.yml.dist
... lines 1 - 3
parameters:
... lines 5 - 22
# set to https on production
secure_channel: http

And as you know, if you add a key here, also add it to parameters.yml.dist:

25 lines app/config/parameters.yml.dist
... lines 1 - 3
parameters:
... lines 5 - 22
# set to https on production
secure_channel: http

Ok, head back to the homepage: http://localhost:8000 and click to checkout. Hey! We're back in http. When you deploy, change that setting to https and boom, your checkout will be secure.

So there's your little trick for forcing https without being forced to hate your life while coding.

Leave a comment!

  • 2017-03-06 weaverryan

    And, the bug in Symfony has been fixed! https://github.com/symfony/...

    Viva the community :)

  • 2017-02-22 weaverryan

    Hi Hannes!

    Ah, thank you! Full code - that is the BEST way to help debug. And, I found the problem thanks to it! It's actually a big in Symfony (https://github.com/symfony/... which has a pending pull request to fix (https://github.com/symfony/....

    Here's the situation:

    A) Whenever you change a routing file (or controller with annotations), Symfony is smart enough to know that it needs to rebuild the routing cache.

    B) But, when you change the parameters.yml (e.g. to change from http to https or vice versa), the routing cache is NOT rebuilt. In other words, when you change your parameter from http to https, the old routing cached (the one that thinks the scheme is set to http) is still set.

    This problem only affects the dev environment. And the fix even in dev is easy - either clear your cache or simply modify any line in your controller file and then refresh. This will trigger your route cache to be rebuilt.

    It's not often you can find a Symfony bug, so... congrats?? :). I've just commented on the pull request to fix it - to see if we can get a version of it merged.

    Cheers!

  • 2017-02-22 Blueblazer172

    Hi Ryan,

    thanks for taking your time on solving this issue.

    I have no other route with the same name :)

    I have put all files in this github project maybe you can find the issue there
    https://github.com/Blueblaz...

    And yes it changes to https if i change it :)

    So what I think is that there must be something wrong with my @Route or generally with the Controller or the twig file

    Regards
    Hannes

  • 2017-02-21 weaverryan

    Hey Blueblazer172 !

    Sorry for my late reply! So, there are two weird things happening, and they seem to conflict with each other:

    A) because you remove secure_channel from parameters.yml and do NOT see any error, it means that Symfony does not see %secure_channel% referenced anywhere. In some ways, that's no surprise - it *seems* like Symfony is not replacing this value for some reason.

    B) But, the fact that the "Scheme" when you run bin/console debug:router order_checkout says HTTP is puzzling. By default (if you don't set schemes), this will say "All". Since it says "http", it means that this value IS being set.

    So, it's a mystery. It seems to be set in some cases, but not others - and that's just not how Symfony's routing system works: one set of routes are compiled and used everywhere. Is it possible that you have a second route defined accidentally with the same name - order_checkout? Also, you mentioned on your first post that when you click "Checkout", you get the wrong behavior, but when you type http:/localhost:8000/checkout and hit enter, it works as expected. Can you post the code for your link? The code for your controller looks perfect. If you change the "schemes" foryour @Route to "https" and run bin/console debug:router order_checkout, does the Schemes value change?

    I'm sure it's something small. This behavior *does* work - it must be some small configuration we're missing!

    Cheers!

  • 2017-02-17 Blueblazer172

    1) http
    2) i dont see any error...
    3) http://imgur.com/a/pxJfg

  • 2017-02-16 weaverryan

    Hey Blueblazer172!

    Hmm, so I just re-checked the code - pulled the code down to this step, and tried it again. It *does* work fine for me. So, a few things to try out :)

    1) If you run php bin/console debug:router order_checkout , what is the value next to Scheme?

    2) If you remove the "secure_channel" from parameters.yml, do you get an error (you should, you should get an error that the parameter secure_channel must be defined). If you don't, it definitely makes me think that - for some reason - the parameter isn't being used.

    3) Could you paste your @Route + controller code here?

    Cheers!

  • 2017-02-15 Blueblazer172

    i did everything but it wont work :/

  • 2017-02-15 Victor Bocharsky

    Yo man,

    Hm, looks like Symfony can't resolve its value. Have you add the "secure_channel" parameter in your app/config/parameters.yml.dist and *also*, what more important, in app/config/parameters.yml file? Also, please, try to clear the cache manually for both dev/prod environments.

    Cheers!

  • 2017-02-15 Blueblazer172

    I'm back :)

    I have the exact code as ryan, but when i click on checkout. this error occurs:

    No route found for "GET /%secure_channel%://localhost/checkout" (from "http://localhost:8000/")

    but when i type localhost:8000/checkout and hit enter is works.

    has to be an easy problem :)