Controlling the World from a Web Browser

Controlling the World from a Web Browser

A presentation on how to use the Raspberry Pi (and a web browser) to control the world (or just about anything in it).

9:30 am
Victoria Computer Club
85A Burnside Rd West (at Wascana), Victoria, BC (map)

Control from the web

Ahh. I see the light.I have shamelessly borrowed this from one of my Camosun labs.


  • Control the Pi from the Web




    • Web Forms
      • Web forms allow us to enter values to be passed to a server
        and from there to a program that the web server
        executes.  See cgi-bin below.
      • This ability to collect input is crucial to much that we do
        on the web.  In fact most of what we see today of the
        dynamic web, cloud services, on line collaboration etc. would,
        obviously, not work if we could not input data.
        Unfortunately there is a down side.  As humans we make
        mistakes, and sometimes we “make mistakes” on purpose in order
        to break, compromise or crack something.  Like your web
      • Validation
        So, as soon as we start writing programs that accept input
        from the “Web” we need to ensure that the input we get is what
        we expect.  And, if not we need to not use the input and
        either complain or ignore it.Given the “Sand Box” environment we are working in, the danger
        is not too high that someone malicious will try to crack our
        cgi-bin scripts, but we need to get into the habit of always
        sanitizing our inputs.  Besides, we, as unlikely as this
        may seem, might make a misteak.
    • Web Servers falls faster apache or IIS?
      • The web server is the server side partner to your web
        browser.  When a web page is requested the server finds
        the web page on its system, probably disk,  and sends it
        back to the requesting browser.
      • Directories of interest
        • /var/www – contains .html files to be served.
        • /usr/lib/cgi-bin  – contains programs to be executed.
    • Common Gateway Interface (CGI)
      • The CGI interface is a way to tell the web server that we
        want it to do more than grab the file from disk and send it to
        us.  We want it to run the file as a program and send the
        results of that program running to us.  The program that
        runs can be written in any language we choose.  The only
        rules are:

        • That the program is in the “cgi-bin” directory.  On
          our Pi this is /usr/lib/cgi-bin/
        • That when the program runs it sends the appropriate header
          telling the web server and browser what the content type
          that it is creating is.  This is a line that looks

        • Assuming your output is html text.
        • The content type could be text/plain or image/png etc.
        •  The \n\n is two new line characters, thus creating a
          blank line.  The blank line is the indication to the
          Server/Browser that the HTTP is done and the (HTML)
          content will now start.  You would create this header
          in Python withprint
          “Content-Type: text/html\n\n”

    • Python – collecting form variables
      • Form variables can be GET or POST.  We will use
        POST.  Post gets to the server via standard in (stdin).
      • Stdin output and error is standard. The rest optionally available.
        • A running program in Linux has stdin, stdout and stderr.
        • In the browser/server system stdin is input from the
          network, stdout is output to the network and stderr is
          output to the network.
        • The following is Python code that will pick up the post
          variables that the web browser form submits and put them
          into the dictionary POST{}:

# Build dictionary of post inputs.
import sys

POST = {}
# Split each input parameter on ampersand.
args ='&')

Or, your life will become much more complex. See Velociraptor.

for arg in args:
# split the name value pairs and add
them to the POST dictionary
    inp = arg.split('=')
    POST[inp[0]] = inp[1]

      • Check validity the On/Off form:

        • Input should be on or off.  Anything else, including
          nothing, should be ignored or an error.
        • We can do this with a series of if/elif/else statements.
    • Executing as Root – Set UID
      • We have two problems if we want to use the GPIO functions
        from a cgi-bin script:

        • GPIO functions must run as root.  We normally do that
          by running the programs like “sudo ./” and this
          works.  But from the web – not so much.

          • We could give the Apache2 user (www-data) sudo
            privileges.  But, do we really want to allow the
            Apache user and thus anyone connecting via the web to our
            machine, complete access to run anything as root.
            This means that if we open our Pi server to to outside
            world then that same outside world could run stuff as
            root.  Not a good thing.I tried that once. It worked once... and only once!
          • Normally when we run a program or a script, it runs with
            the privileges of the person who runs it, not the
            privileges of the owner.We can set our script to what is called suid.  And
            when the script is run it will run with the privileges of
            the owner of the file, not the “runner”.   An
            example is the Linux passwd command.  This allows a
            plain user to change a value in a root read only
            file.  That value is your password.

            -rwsr-xr-x. 1
            root root 30768 Feb 22  2012 /usr/bin/passwd

            And, the passwd program is very carefully written to
            ensure that you may only change your password.  Not
            someone else’s.

          • But there is another problem.  Scripts can have
            security issues in Linux in the way they are run, see next
            point.  So in general Linux will refuse to run any
            script in setuid mode.  In order to solve this
            problem we can use a compiled C wrapper.  The
            compiled wrapper in turn runs the script.
      • C Wrapper
          • Why – security issues.  From the Perl Security man
            page (man perlsec)
            Beyond the obvious problems that stem from giving special
            privileges to
            systems as
            flexible as scripts, on many versions of Unix, set-id
            scripts are
            inherently insecure right from the start.  The
            problem is a
            race condition in
            the kernel.  Between the time the kernel opens
            file to see which
            interpreter to run and when the (now-set-id)
            interpreter turns
            around and reopens the file to interpret it, the file
            in question may
            have changed, especially if you have symbolic links on
            your system.
          • How – Build a C Wrapper
            • The C wrapper is compiled so it does not have the
              security issues that a plain script has.  Still it
              is running a plain script and we must ensure that the
              script is secure, inputs are checked and that it can
              only do what we intended it to do.  The C wrapper:      #define REAL_PATH “/path/to/script”
              main(ac, av)
              char **av;
              execv(REAL_PATH, av);
            • ac is the number of arguments passed to the C program
              and av is a pointer to an array of arguments. The O/S
              handles setting this up so that you have the arguments
              available that were passed to the script.
            • The first line is not I repeat NOT a
              comment.  This is C, not Python. It is a
              declaration of a constant. You need to change this to
              the fully qualified path and name of the script you want
              to run, and this is the only thing you need to change in
              this program. E.g. /usr/lib/cgi-bin/
            • This is C so you need to compile it. gccmywrapper.c -o mywrapperwill compile the C
              source file mywrapper.c into the executable file
              mywrapper.  Feel free to change the names to names
              that make sense in this context.   In fact –
              please do that.

              By convention C executables on Linux systems do not have
              an extension.

              Don’t mess the naming up. The compiler will happily
              compile your source file to an executable of the same
              name E.g. gcc -o foo.c foo.c will overwrite your source
              with an executable incorrectly named.

            • Both the compiled C program and the script need to be
              executable and the script needs a Shebang.
            • Change the owner of the compiled C program to root
              before you use chmod to set the setuid bit.
              • chown
                root:root mywrapper
              • chmod
                4755 mywrapper
      • Log Files of interest:
        • /var/log/apache2/error_log – Error logs.
        • /var/log/apache2/access_log – Logs of pages served.
      • .htaccess   If time permits – probably next time.
      • You may be tempted to open up your Pi to the World.
        Don’t!  Not yet.

        • Security issues to opening Pi to the world.
            • ssh
            • password
            • sudo
            • iptables
            • vnc




Web form – this sends the request, on or off

<title>Turn something on or off</title>
<h1>Turn Something On and Off</h1>
<form action=”/cgi-bin/onoff” method=”post”>
ON: &nbsp;<input name=”onoff” value=”On” type=”radio”
OFF: <input name=”onoff” value=”Off” type=”radio”
<input type=”submit”>

Wrapper – this allows the suid cgi script to be executed securely

#define REAL_PATH “/usr/lib/cgi-bin/”
main(ac, av)
char **av;

execv(REAL_PATH, av);

python back end – this does the work

#! /usr/bin/python

# Import the Python gpio library and the system library
import RPi.GPIO as GPIO
import sys

print “Content-type: text/html\n\n”
print “<html><head><title>This is a

# Build dictionary of post inputs.
POST = {}
args =‘&’)
for arg in args:
t = arg.split(‘=’)
POST[t[0]] = t[1]

# Get the input value from the POST[] array
inp = POST[‘onoff’]

# Determine on or off and which message to display.
if inp == “On”:
print “<h1>Turned On</h1>”
onOff = 1
elif inp == “Off”:
print “<h1>Turned Off</h1>”
onOff = 0
print “How do I do that?”

# Turn off warnings

# This is the gpio pin I have the led connected to
pin =  5

#  Set the pin number mode to Broadcom rather than connector

# Set the choosen pin to be an output and set the ouput true, on.
GPIO.setup(pin, GPIO.OUT)
GPIO.output(pin, onOff)

# Display the state of the pin.
print GPIO.input(pin);



  • What is the purpose of web forms?
  • How important are web forms to our experience of the web?
  • Discuss why and how to validate input data.
  • What is a web server and which web server are we using on the
  • /var/www  and /usr/lib/cgi-bin  are
    directories  on the Pi.  What are each of the
    directories used for?
  • What is the purpose of the CGI?
  • What http line do we have to output before we output html?
  • Where do we get the submitted POST variables from?
  • What is set uid?  Why do we use it.
  • What do we have be extremely careful with when we create suid
  • What is the purpose of the C wrapper?
  • Why do we have to compile the C wrapper program?
  • List two Apache log files, the directories they will be found
    in and what will be found in them.
  • Why do we care about the log files?
  • Opening up the Pi to the outside world.  Why should we
    not do this yet?
  • What is the purpose of .htaccess?
  • What files do we need to create to make .htaccess work and
    what file do we need to modify?