Thursday, February 14, 2013

Character encoding on requests and Sakai

So an issue has come to light in our local Sakai deployment with character encodings. Some request were being incorrectly interpreted to be encoded using ISO-8859-1 instead of UTF-8. Before I explain what was going on here is some background.

HTML specification

The HTML specification has stuff about encodings and the short of it is that if you are making a request to a web server using a GET then you shouldn't have any foreign characters in the URL, you should just be using ASCII. In practice you can use foreign characters if you UTF-8 encode them as that's commonly assumed to be the used encoding by browsers, but it's not part of the standard. For example here is Google Chrome displaying a URL with UTF-8 encoded characters at the end.
The URL in the referencing page ends 096/%E1%B8%9F%C8%AF%E1%B9%99%C3%AF%C4%97%C4%A1%E1%B9%85.%E1%B9%AB%E1%BA%8B%E1%B9%AB which is the URL encoded version of the UTF-8 characters "096/ḟȯṙïėġṅ.ṫẋṫ".
If you have a browser making POSTs to the server, then you have a choice of two ways of submitting the data, application/x-www-form-urlencoded (which is the default on a
tag) ormultipart/form-data. If you are using characters in your form outside ASCII then you should use multipart/form-data as browsers don't typically say what encoding they are using when performing application/x-www-form-urlencoded, although lots of people assume it to be UTF-8.

Servlet request decoding.

When a request comes in to Tomcat a HttpServletRequest object is built and this includes the raw request path as well as a decoded one. Commonly containers such as Tomcat will use ISO-8859-1 to decode the path although this can be overridden in configuration. If the request is a POST and the content type is application/x-www-form-urlencoded then the container must also make the form data available as parameters and it will decode any characters using the character set supplied by the browser, however most browsers don't appear to sent a character set when submitting urlencoded POSTs and so it falls back to the containers default which in the case of Tomcat is ISO-8859-1, this can be overridden by calling ServletRequest.setCharacterEncoding(String).

If a request is a POST and the content type is multipart/form-data then the container doesn't do any decoding and it's up to the application to decode the body of the request and extract any parameters from it. This is one reason why people adopt application/x-www-form-urlencoded forms as it means they don't have to deal with parsing the requests, although there are lots of frameworks that help with this.

Part of the reason for only having the container decode application/x-www-form-urlencoded requests is that multipart/form-data are often used when file uploads are performed and may be large in which case you have to be careful about when you consume the upload and where you put the data.

Sakai and character sets

So Sakai supports unicode and uses UTF-8 as it's default encoding. But it does this through configuring all requests to use UTF-8. So in the Tomcat connector configuration the URI encoding is specified to be UTF-8 instead of ISO-8859-1 and the Sakai request filter which preprocesses all requests to Sakai (RequestFilter) sets the request encoding (if not already set) to be UTF-8 for any URL encoded form submissions.

This means that you can create a form in Sakai and leave the encoding as application/x-www-form-urlencoded and because the RequestFilter sets the encoding to UTF-8 everything works, really this is a bug and the form should be changed to use the correct encoding but as it generally works nobody notices. The more technically correct solution would be to have the original form submission made using multipart/form-data as this way you normally get the character encoding used by the browser in submitting the request.

Back to the problem...

We had a filter that was doing some authentication (OAuth) before the standard Sakai request filter. It was all working correctly but we started seeing bugs when people submitted some requests with foreign characters in them. After some investigation it turned out that the cause was the OAuth filter and through it's inspection of the request parameters.

The OAuth filter needs to look at the request parameters to extract any authentication information but in doing so it causes the servlet container to decode all the request paramaters. By default (and following the spec) a servlet container will decode URL encoded parameters according to the ISO-8859-1 character set. Once decoded the parameters remain decoded with that initial character set.

References

Monday, October 29, 2012

Allow Cyberduck to login to Amazon S3

If you have created a user in the Amazon IAM Console and wish to allow them to use Cyberduck to connect to Amazon S3 you need to grant them the ability to list all the buckets. This is done by applying a policy on the user of:


{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListAllMyBuckets"
      ],
      "Resource": "arn:aws:s3:::*"
    }
  ]
}


This means they can see all the buckets that exist on your account but can't actually see then contents of them without getting some policies applied on specific buckets.

This all comes about from the way Cyberduck does it's initial login to Amazon S3.

Update: If you just connect directly to the bucket using it's bucket name as part of the hostname (eg www.example.com.s3.amazonaws.com), you don't need to grant this permission but you do get an SSL issue.

Thursday, July 05, 2012

virsh vol-upload error

I was attempting to upload a file into a volume using virsh with libvirt to a qemu/kvm hypervisor storage pool and I was getting:

vol-upload error: unknown procedure: 208


Turns out this is because the remote libvirt version was 0.7.5 and upload support wasn't added until 0.9.0. Hope this helps someone else.

Tuesday, June 05, 2012

tftpd on Mac OS X

Mac OS X comes with a TFTP daemon which is all setup to use launchctl, the only issue is it's disabled by default. launchctl allows you to override the disabled entry when loading the file so to use the TFTP daemon:

sudo launchctl load -w /System/Library/LaunchDaemons/tftp.plist

Then it will serve files up from /private/tftpboot/ just make sure they are readable by all. To shutdown the daemon just run:

sudo launchctl remove com.apple.tftpd

Monday, May 28, 2012

svn diff of just modified files

I was working against a project in which I had a few conflicts, but wanted to generate a patch containing some of the changes to that project, in this case I wanted files I had modified but not the ones that had conflicts. In short svn doesn't provide a way todo this but using a few additional tools it works fine:


svn st | awk '$1 == "M"{print $2;}'| xargs svn diff


Monday, March 19, 2012

Random Skype Disconnections

Recently I'd started having problems with Skype disconnecting several minutes into a call and losing internet access in other applications as well. This initially was only happening with Skype on the iPad so I put it down to bugginess on the iOS version of Skype crashing the router or something on the iPad, however I just had the same disconnection symptoms after about 5 minutes of a video call on my Mac. This time I went to the router's administration page and found that the router (Speedtouch ST780) hadn't recently rebooted but in the event log it said:

Mar 19 22:04:10IDS dos parser : udp flood (1 of 1) : 192.168.1.69 80.229.225.26 1018 UDP 65177->45237

Now this tied in exactly with the time of the call dropping so it looks like Skype is triggering the udp flood detection on the router. There isn't a simple way to disable this through the web interface. So you have to telnet to the router and issue the commands:


ids config state=disabled
saveall


which disables the intrusion detection system. I'm not too worried about this as the router doesn't have any real services listening on it's WAN IP and I trust all the internal clients. Anyway, hopefully problem solved for now.

Saturday, December 31, 2011

General Logging in MySQL



I was looking to investigate some connection problems we were having to a MySQL database and wanted to be sure that the client was connecting to correct machine. MySQL 5.1 doesn't provide this information on it's own, but it is contained in the general log. However the log contains all statements executed against MySQL so will have a performance impact on a production machine. The general log can be enabled/disabled while MySQL is still running which means you can enable the logging, capture the events your interested in and then disable it again. Todo this connect to MySQL as a user who can set GLOBALS (typically root) and run:

mysql> SET GLOBAL general_log_file='/var/log/mysql.log';
Query OK, 0 rows affected (0.00 sec)

mysql> SET GLOBAL general_log = 1;
Query OK, 0 rows affected (0.00 sec)

Then the file /var/log/mysql.log should start filling up with statements. Once your done disable the general log:

mysql> SET GLOBAL general_log = 0;
Query OK, 0 rows affected (0.01 sec)

In my case I was looking for connection messages so a simple grep pulled out the lines I was interested in:

grep Connect /var/log/mysql.log | less